Text Truncated. Only the first 1MB is shown below. Download the file for the complete contents.
QD 1 - 32-Bit QuickDraw: Version 1.2 Features
QuickDraw
Written by: Guillermo Ortiz April 1990
This Technical Note describes the changes and enhancements to 32-Bit QuickDraw from version 1.0 (as shipped on the original Color Disk) to version 1.2, which ships with System Software 6.0.5 and later. This Note assumes familiarity with Inside Macintosh, Volume V, Color QuickDraw, and 32-Bit QuickDraw release notes.
32-Bit QuickDraw
Version 1.0 of 32-Bit QuickDraw shipped in May 1989 in response to the growing need for Color QuickDraw support for direct color devices and pictures (PICT2) and video boards for large-screen monitors which require 32-bit addressing for black and white operation. This original version of 32-Bit QuickDraw was a separate file that had to be copied manually into the System Folder. With the introduction of the Macintosh IIci, Apple put 32-Bit QuickDraw into ROM. Now System Software 6.0.5 and later offer 32-Bit QuickDraw as an integral part of the System Software which can be installed by the standard Installer (although the file is still separate).
This Note describes the changes and enhancements in version 1.2 of 32-Bit QuickDraw from version 1.0. Beginning with version 1.2, QuickDraw functionality is identical on all Color QuickDraw machines, including all the performance improvements which were originally only available in the IIci ROM.
New Features (In No Particular Order)
PICTs Contain Font Name Information
Every time you draw text inside of an _OpenPicture and _ClosePicture pair, QuickDraw stores the name of the current font and uses it when playing back the picture. The opcode used to save this information is $002C and its data is as follows:
PictFontInfo = Record
length : Integer; { length of data in bytes }
fontID : Integer; { ID in the source system }
fontName : Str255;
END;
QuickDraw only saves this information one time for each font used in a picture. When QuickDraw plays back a picture, it uses the fontID as a reference into the list of font names which are used to set the correct font on the target system.
For example, the following code:
GetFNum('Venice', theFontID); { Set a font before opening PICT}
TextFont(theFontID);
pHand2 := OpenPicture (pictRect);
MoveTo(20,20);
DrawString(' Better be Venice');
GetFNum('Geneva', theFontID);
TextFont(theFontID);
MoveTo(20,40);
DrawString('Geneva');
GetFNum('New York', theFontID);
TextFont(theFontID);
MoveTo(20,60);
DrawString('New York');
GetFNum('Geneva', theFontID);
TextFont(theFontID);
MoveTo(20,80);
DrawString('Geneva');
ClosePicture;
generates a picture containing font information like this:
OpCode 0x002C {9,
"0005 0656 656E 6963 65"} /* save current font */
TxFont 'venice'
DHDVText {20, 20, " Better be Venice"}
OpCode 0x002C {9, /* save next font name */
"0003 0647 656E 6576 61"}
TxFont 'geneva'
DVText {20, "Geneva"}
OpCode 0x002C {11, /* ditto */
"0002 084E 6577 2059 6F72 6B"}
TxFont 'newYork'
DVText {20, "New York"}
TxFont 'geneva' /* second Geneva does not
need another $002C guy */
DVText {20, "Geneva"}
This feature works regardless of the type of picture being saved, including old style PICTs in a black and white port. Using _OpenCPicture instead of _OpenPicture to start a recording session results in the same functionality.
Direct PixPat Structures Now Supported
QuickDraw now supports 16-bit and 32-bit per pixel PixPat structures (patType = 1). In addition, it now supports a new patType (3) which uses dithering whenever 16-bit or 32-bit pixel patterns are displayed on indexed devices.
Direct 'cicn' Resources Now Supported
QuickDraw now supports 16-bit and 32-bit per pixel 'cicn' resources. The 16-bit per pixel is particularly cool since you save the space required for an 8-bit 'clut'.
GWorlds Can Now Be Allocated in MultiFinder Temporary Memory
You can now use the new useMFTempBit (bit 2) in a call to NewGWorld as an option to allocate pixels in MultiFinder temporary memory. In addition, you can now allocate screen buffers in MultiFinder temporary memory using the following routine, defined in Pascal and C:
FUNCTION NewTempScreenBuffer (globalRect: Rect; purgeable: BOOLEAN;
_CopyBits now supports the ditherCopy transfer mode whenever the destination device is between one and eight bits per pixel, regardless of the depth of the source image. With this support, an eight-bit image can now be approximated on a one-bit or a four-bit device by using error diffusion. Furthermore, an eight-bit image could also be dithered to a different set of 256 colors or a four-bit image could be dithered to an eight-bit device that does not have the desired colors.
32-Bit Addressed PixMap Structures
Version 1.2 defines a new pmVersion (baseAddr32 = 4) for 32-bit pointer baseAddr values. The baseAddr of such PixMap structures is treated as a 32-bit address, so no stripping or address translation is performed on it in 32-bit mode. This is a specially useful feature when the base address of a PixMap points to a NuBus™ address, for example in a video grabber board.
A new call, Pixmap32Bit, is now available to inquire if a given PixMap requires 32-bit addressing.
Version 1.2 updates GetPixBaseAddress to return the address of any PixMap. The routine does the right address translation or stripping for all PixMap structures, including screen devices, unlocked GWorlds, and 32-bit addressed PixMap structures. The address it returns is only valid in 32-bit addressing mode. Also unless the PixMap is locked and made unpurgeable, the address returned by GetPixBaseAddress is only valid until any call to QuickDraw or the toolbox is made.
_CopyBits from Screen Devices
The picture recording mechanism has changed so that if you call _CopyBits while recording a picture with the source PixMap being a screen device, the data is correctly accumulated into the picture. Note that if the screen being copied is not the main screen, then the PixMap must be a 32-bit addressed PixMap. No auxiliary screen buffer is allocated if the source rectangle covers only one screen.
New Picture Recording Trap
Version 1.2 adds a new call, _OpenCPicture, to create pictures that contain information regarding the native resolution of the recorded image. When QuickDraw draws this picture, it scales the image to the resolution of the target device. Applications that need to scale the images directly can also access this information.
FUNCTION OpenCPicture(VAR CPictInfo:CPictRecord):PicHandle;
For information on bug fixes in the System Software 6.0.5 release of 32-Bit QuickDraw (version 1.2), please refer to the System Software 6.0.5 Change History, which is available on the Developer CD Series, AppleLink in the Developer Services Bulletin Board (Developer Services: Macintosh Developer Technical Support: System Software), and the Apple FTP site on the Internet in the ~ftp/pub/dts/sw.license.
Note that the dispatching mechanism for the new _QDOffscreen calls is slightly different than previously documented; it now requires that the high word passed in D0 contain the total length of the parameters (in bytes). The reason for this change is that if the call is made in an earlier version of 32-Bit QuickDraw, the system can strip the parameters from the stack and return QDError set to the caller (instead of crashing).
Further Reference:
• Inside Macintosh, Volume V, Color QuickDraw
• 32-Bit QuickDraw Release Notes (available from APDA)
• System Software 6.0.5 Change History
• d e v e l o p, Issue I
NuBus is a trademark of Texas Instruments.
QD 2 - BitMapToRegion : So Many Bitmaps, So Little Time
QuickDraw
Revised by: Rich Collyer December 1989
Written by: Rick Blair April 1988
This Technical Note discusses the routine BitMapToRegion, which converts a bitmap to a region, and is available in the 32-Bit QuickDraw INIT and from Apple Software Licensing.
Changes since October 1989: Added trap definitions for developers using the 32-Bit QuickDraw version of this routine without the correct MPW include file.
The following routine is now available to convert a bitmap to a region:
FUNCTION BitMapToRegion(region:RgnHandle; bMap:BitMap): OSErr;
If you are using the 32-Bit QuickDraw version of this routine without the correct MPW include file, then you need to include one of the following definitions:
Pascal
FUNCTION BitMapToRegion (region: RgnHandle; bMap: BitMap): OSErr;
The region will be built so that all one bits in bMap are inside the region and all zero bits are outside of it.
As with all QuickDraw calls which change a region, BitMapToRegion requires you to pass an existing region (originally created by _NewRgn). If the region cannot be built due to an insufficient heap space or a size greater than 32K, then the routine will return an appropriate error code and the region will be empty. If the region would have exceeded 32K, the error will be rgnTooBigErr (-500).
This function is useful for a number of situations where you have (or can produce) a bitmap representing an area. You can use _CalcMask to produce such a bitmap. Once you have a region, you can perform region operations (i.e., _PtInRgn, _UnionRgn, or _InsetRgn) or call _DragGrayRgn, for example.
This call is part of the 32-Bit QuickDraw INIT ($A8D7). If you do not wish to depend on 32-Bit QuickDraw, then you can obtain a version of BitMapToRegion in MPW object format which can be linked into an MPW program, by contacting Apple Software Licensing:
Apple Software Licensing
Apple Computer, Inc.,
20525 Mariani Avenue, M/S 38-I
Cupertino, CA, 95014
(408) 974-4667
AppleLink: SW.LICENSE
If you licensed the older version of this routine, BitMapRgn, contact Software Licensing about receiving an updated version. We recommend you update your application to use the new version as soon as possible.
The new version is now named BitMapToRegion to be consistent with the version in 32-Bit QuickDraw and the MPW interfaces. In addition, BitMapToRegion offers new features. You can now pass a one-bit pixelmap which has been coerced to a bitmap. If you pass a pixelmap which is too large, then you will get a pixmapTooDeepErr (-148) error. You can also pass the portBits of a window, much like you would do with a call to _CopyBits.
There is a potential problem with this routine, since MPW 3.1 include files contain information about 32-Bit QuickDraw. If you want BitMapToRegion to be available on all machines, then you must use the object file from Software Licensing. The problem is that when you compile your application with MPW 3.1 or later, the 32-Bit QuickDraw version gets preference over the object file. You must comment out the routine in the include files if you want to use the object file. If you only care about using BitMapToRegion on machines running 32-Bit QuickDraw, then you need not do anything.
QD 3 - Color Cursor Cursing : A Leading Cause
QuickDraw
Revised by: Alan Mimms October 1989
Written by: Alan Mimms June 1989
Working with color cursors you create from scratch can cause headaches. This Technical Note may help a bit.
Changes since June 1989: Added a warning about purgeable 'clut' resources.
If you’re building an application that creates color cursors, you may encounter some quirks present in Color QuickDraw that manifest themselves in hard-to-understand ways.
If your cursor is, say, 15 pixels tall and 9 pixels wide, you might be tempted to use these values for the bounds.bottom and bounds.right, respectively, in your cursor’s pixel map. Don’t. The problem is that when the cursor’s image needs to be expanded (i.e., when you specify a two bit-per-pixel cursor and the mouse pointer is on an eight-bit screen) the _SetCCursor trap rounds the width of the pixel map in such a way that you’ll get only the space required for a 15 by 8 pixel map allocated for the expanded cursor data. When the cursor’s image is expanded into this too-small expanded cursor data handle as a 15 by 9 pixel map, something in your heap will get munched.
The cure is simple. Make certain that you always specify that the pixmapHandle^^.bounds be 16 by 16. This will cause _SetCCursor to properly allocate the expanded data area, and all will be well in the land. Since the amount of data drawn for a cursor is specified by the cursor’s pixel values and 'clut' resource, trying to save a few bytes by making the bounds rectangle smaller than 16 by 16 wouldn’t have been very helpful anyway.
Another potential problem is with the color cursor’s color table. If you load the color table from a 'clut' resource using _GetCTable, you should make sure that the 'clut' is marked non-purgeable while the color cursor is in use. If you do not take this precaution, bombs will occur if your 'clut' gets purged at in inopportune time.
QD 4 - Colorizing With CopyBits
QuickDraw
Revised by March 1988
Written by: Chris Derossi November 1987
Inside Macintosh Volume V states that the foreground and background colors are applied to an image during a CopyBits or CopyMask call. Accidental use of this feature can create bizarre coloring effects. This note explains what happens, how to avoid problems, and how to use it.
What Happens
Color QuickDraw has a feature that will allow you to convert a monochrome image to a color image. During a CopyBits or CopyMask call, if the foreground and background colors are not black and white, respectively, Color QuickDraw performs the following operation on every pixel being copied:
NOTE: color table index = pixel value
s = color table index of source pixel
fg = color table index of foreground color
bg = color table index of background color
ColoredPixelValue = (NOT(s) AND bg) OR (s AND fg)
If your source image contains only black and white pixels, then all black pixels would become the foreground color and all white pixels would become the background color. This is because the color table index for white is all zeros and the color table index for black is all ones.
For example, suppose your source image was a 4-bit deep color PixMap. Then the color table index for white (in binary) is 0000 and the index for black is 1111. And let’s suppose that your foreground color is green with an index of 1101 while your background color is red with an index of 0011. Then for the black pixels, the above procedure produces:
ColoredPixelValue = (NOT(1111) AND 0011) OR (1111 AND 1101)
1101 = ( 0000 AND 0011) OR (1111 AND 1101)
And the operation on the white pixels yields:
ColoredPixelValue = (NOT(0000) AND 0011) OR (0000 AND 1101)
0011 = ( 1111 AND 0011) OR (0000 AND 1101)
Possible Problems
This colorizing will only work on 2-color (i.e. black and white) images, and then only if those colors occupy the first and last entries in the color table. Trying to colorize colors that are not the first and last color table entries will yield unexpected results.
This is mainly due to the fact that the colorizing algorithm uses a pixel’s color table index value rather than its actual RGB color. To illustrate this, let’s assume that foreground and background colors are as above, and your image contains yellow with a color table index of 1000. The colorizing operation would give:
ColoredPixelValue = (NOT(1000) AND 0011) OR (1000 AND 1101)
1011 = ( 0111 AND 0011) OR (1000 AND 1101)
Since the color table may have any RGB color at the resulting index position, the final color may not even be close to the source, foreground, or background colors.
Similar things occur if you are trying to colorize a black and white image when white and black do not occupy the first and last positions in the color table.
The bottom line rules for CopyBitsing in a color environment are these:
• Thou shalt set thy background color to white and thy foreground color to black before calling CopyBits or CopyMask, unless thou art coloring a monochrome image.
• Thou shalt, when colorizing, make sure that the first color table entry is white and the last color table entry is black.
The second rule is easy to follow because the default color tables are constructed properly, and if you are using the Palette Manager (and you are, right?) then it will make sure that the color tables obey this rule.
How To Colorize—An Example
This code fragment shows how to implement a color fill, like the paint bucket in MacPaint. It relies on three main things: SeedCFill for calculating the fill area, CopyMask for actually changing the bits, and QuickDraw colorizing.
The variable offBits is an offscreen BitMap (not a PixMap) with bounds = myWindow^.portRect. SeedCFill effectively creates, in the offscreen BitMap, a monochrome image of the bits that we want to paint. Since offBits contains the exact bits that we want to paint, it is used as both the source image and the mask for CopyMask.
By setting the foreground color to the desired paint color, the result is a colorized version of the mask (the paint area) being copied onto the window’s PixMap without affecting any other bits.
Further Reference:
• Color QuickDraw
QD 5 - Displaying Large PICT Files
QuickDraw
Revised by: March 1988
Written by: Rick Blair July 1987
Now that we have scanners and other massive-picture producing types of applications, there is a need to address the problem of how to display a PICT format object that is bigger than a current PICT resource is allowed to be. Note that this technique applies equally well to version 1 and version 2 (word-opcode) pictures as produced by the Macintosh II.
Future Compatibility
Think of the handle returned by a GetResource('PICT',ID) as a “handle” in the more general sense of being an abstract “tag”—something that the ROM routines can use to draw the picture with. Don’t assume that the entire picture has been read into memory or that you can directly read any bytes beyond the basic Picture record structure (picSize followed by picFrame). Someday we may provide a mechanism for the resource to be disk- instead of memory-based. The QuickDraw bottleneck procedures will know how to get data from and put data into the pictures in any case.
Spooling from a PICT file
In order to display pictures of arbitrary size, your application should be able to import a QuickDraw picture from a file of type PICT. This is the file produced by a “Save as…” from MacDraw with the PICT option selected.
What follows is a small program fragment that demonstrates how to spool in a picture from [the data fork of] a PICT file. The picture can be larger than the historical 32K resource size. See technical note #88 if you are unfamiliar with the Signal mechanism. We assume that a CatchSignals has been done before GetandDrawPICTFile is called.
MPW Pascal Example
{the following variable must be at the top level}
VAR
globalRef : INTEGER; {refNum of the file to read from}
{the following procedure must be at the top level}
DrawPicture(myPicture,&(**myPicture).picFrame); /*draw the picture*/
err = GetFPos(globalRef,&filePos);/*get position for check*/
if (err != noErr) return err;
err = FSClose(globalRef);
if (err != noErr) return err;
DisposHandle((Handle)myPicture);
(*qd.thePort).grafProcs = savedProcs;/*restore the procs*/
/*Check for errors. if there wasn't enough room,*/
/*DrawPicture will abort; the FILE position mark*/
/*won't be at the end of the FILE.*/
if (filePos != myEOF) return abortPICT;
else return noErr;
} /*if (reply.good) */
} /* GetDrawPICTFile */
More on Picture Compatibility
Many applications already support PICT resources larger than 32K. The 128K ROMs (and later) allow pictures as large as memory (or spooling) will accommodate. This was made possible by having QuickDraw ignore the size word and simply read the picture until the end-of-picture opcode was reached.
For maximum safety and convenience, let QuickDraw generate and interpret your pictures.
While Apple has provided you with the data formats that allow you to read or write picture data directly, we recommend that you always let DrawPicture or OpenPicture and ClosePicture process the opcodes.
One reason to read a picture directly by scanning the opcodes would be to disassemble it to, for example, extract a Color QuickDraw pixel map to save off in a private data structure. This shouldn’t normally be necessary.
If you do look at the picture data be sure and check the version information. You may want to put up an alert in your application that indicates to the user when a picture was created using a later version of the picture format than your application recognizes, letting them know that some elements of the picture cannot be displayed. If the version information indicates a QuickDraw picture version later than the one recognized by your application, your program should skip over the new opcodes and only attempt to parse the ones it knows.
As with reading picture data directly, it is best to use QuickDraw to create data in the PICT format. If you do need to create PICT format data directly, it is essential that you use the latest opcode specifications and that you thoroughly test the data produced on both color and black and white Macintosh machines. Contact Macintosh Developer Technical Support if you are not sure that you have the latest specifications.
Apple does not guarantee that a picture which wasn’t produced by QuickDraw will work.
Further Reference:
• QuickDraw
• Technical Note M.IM.PictureOpcodes —
Internal Picture Format
• Technical Note M.PT.Signals —
Signals
QD 6 - Every Picture [Comment] Tells Its Story, Don’t It?
QuickDraw
Revised by: March 1988
Written by: Rick Blair November 1987
Application-specific picture comment conflict and registration is addressed, along with Developer Technical Support’s method for solving it.
I will assume that the nature and usefulness of picture comments are already well known. The problem I am addressing is that, as it stands, developers must register their comments with us (Developer Technical Support) or run the risk of using the same comments as those used by Apple or within another third party product.
The idea here is to provide a “metacomment” which will contain information about which application owns the comment as well as the comment itself:
data = application signature (i.e. 'MPNT' for MacPaint) = 4 bytes
application “local” kind = 2 bytes
comment data = n bytes
In this way each comment may be specific to an application. It is still up to a developer to publish information about the comments they have defined if they wish them to be understood and used by other programs.
Previously assigned and registered comments will still be valid. This means that those defined for the LaserWriter or MacDraw, for instance, will retain their normal meaning.
Suppose your application (creator = 'MYAP') wanted to define a comment. The appearance of the comment would be as follows (assuming you chose 128 to be the “local” kind for the comment):
kind = 100; data = 'MYAP' [4 bytes] + 128 [2 bytes] + additional data
Further Reference:
• QuickDraw
• Technical Note M.IM.PictComments —
Optimizing for the LaserWriter—Picture Comments
QD 7 - Off-Screen Bitmaps
QuickDraw
Revised by: Jon Zap & Forrest Tanaka June 1990
Written by: Jim Friedlander & Ginger Jernigan July 1985
This Technical Note provides an example of creating an off-screen bitmap, drawing to it, and then copying from it to the screen.
Changes since April 1990: Clarified the section on window updates with off-screen bitmaps to explicitly limit these updates to your own windows.
The following is an example of creating and drawing to an off-screen bitmap, then copying from it to an on-screen window. We supply this example in both MPW Pascal and C.
MPW Pascal
First, let’s look at a general purpose function to create an off-screen bitmap. This function creates the GrafPort on the heap. You could also create it on the stack and pass the uninitialized structure to a function similar to this one.
FUNCTION CreateOffscreenBitMap(VAR newOffscreen:GrafPtr; inBounds:Rect) : BOOLEAN;
VAR
savePort : GrafPtr;
newPort : GrafPtr;
BEGIN
GetPort(savePort); {need this to restore thePort after OpenPort changes it}
newPort := GrafPtr(NewPtr(sizeof(GrafPort))); {allocate the GrafPort}
IF MemError <> noErr THEN BEGIN
CreateOffscreenBitMap := false; {failed to allocate it}
EXIT(CreateOffscreenBitMap);
END;
{
the OpenPort call does the following . . .
allocates space for visRgn (set to screenBits.bounds) and clipRgn (set wide
open)
sets portBits to screenBits
sets portRect to screenBits.bounds
etc. (see IM I-163,164)
side effect: does a SetPort(offScreen)
}
OpenPort(newPort);
{make bitmap exactly the size of the bounds that caller supplied}
WITH newPort^ DO BEGIN {portRect, clipRgn, and visRgn are in newPort}
portRect := inBounds;
RectRgn(clipRgn, inBounds); {avoid wide-open clipRgn, to be safe}
RectRgn(visRgn, inBounds); {in case inBounds is > screen bounds}
END;
WITH newPort^.portBits DO BEGIN {baseAddr, rowBytes and bounds are in
newPort}
bounds := inBounds;
{rowBytes is size of row It must be rounded up to even number of bytes}
ClosePort(oldOffscreen); { dump the visRgn and clipRgn }
DisposPtr(oldOffscreen^.portBits.baseAddr); { dump the bits }
DisposPtr(Ptr(oldOffscreen)); { dump the port }
END;
Now that you know how to create and destroy an off-screen bitmap, let’s go through the motions of using one. First, let’s define a few things to make the _NewWindow call a little clearer.
CONST
kIsVisible = true;
kNoGoAway = false;
kMakeFrontWindow = -1;
myString = 'The EYE'; {string to display}
Here’s the body of the test code:
VAR
offscreen : GrafPtr; {our off-screen bitmap}
ovalRect : Rect; {used for example drawing}
myWBounds : Rect; {for creating window}
OSRect : Rect; {portRect and bounds for off-screen bitmap}
myWindow : WindowPtr;
BEGIN
InitToolbox; {exercise left to the reader}
myWBounds := screenBits.bounds; { size of main screen }
InsetRect(myWBounds, 50,50); { make it fit better }
DestroyOffscreenBitMap(offscreen); {remove the evidence}
WHILE NOT Button DO; {give user a chance to see the results}
END.
MPW C
First, let’s look at a general purpose function to create an off-screen bitmap. This function creates the GrafPort on the heap. You could also create it on the stack and pass the uninitialized structure to a function similar to this one.
Boolean CreateOffscreenBitMap(GrafPtr *newOffscreen, Rect *inBounds)
{
GrafPtr savePort;
GrafPtr newPort;
GetPort(&savePort); /* need this to restore thePort after OpenPort */
newPort = (GrafPtr) NewPtr(sizeof(GrafPort)); /* allocate the grafPort */
if (MemError() != noErr)
return false; /* failed to allocate the off-screen port */
/*
the call to OpenPort does the following . . .
allocates space for visRgn (set to screenBits.bounds) and clipRgn (set wide
open)
sets portBits to screenBits
sets portRect to screenBits.bounds
etc. (see IM I-163,164)
side effect: does a SetPort(&offScreen)
*/
OpenPort(newPort);
/* make bitmap the size of the bounds that caller supplied */
newPort->portRect = *inBounds;
newPort->portBits.bounds = *inBounds;
RectRgn(newPort->clipRgn, inBounds); /* avoid wide-open clipRgn, to be safe */
RectRgn(newPort->visRgn, inBounds); /* in case newBounds is > screen bounds */
/* rowBytes is size of row, it must be rounded up to an even number of bytes */
if (MemError()!=noErr) { /* check to see if we had enough room for the bits */
SetPort(savePort);
ClosePort(newPort); /* dump the visRgn and clipRgn */
DisposPtr((Ptr)newPort); /* dump the GrafPort */
return false; /* tell caller we failed */
}
/* since the bits are just memory, let's clear them before we start */
EraseRect(inBounds); /* OpenPort did a SetPort(newPort) so we are ok */
*newOffscreen = newPort;
SetPort(savePort);
return true; /* tell caller we succeeded! */
}
Here is the function to get rid of an off-screen bitmap created by the previous function:
void DestroyOffscreenBitMap(GrafPtr oldOffscreen)
{
ClosePort(oldOffscreen); /* dump the visRgn and clipRgn */
DisposPtr(oldOffscreen->portBits.baseAddr); /* dump the bits */
DisposPtr((Ptr)oldOffscreen); /* dump the port */
}
Now that you know how to create and destroy an off-screen bitmap, let’s go through the motions of using one. First, let’s define a few things to make the _NewWindow call a little clearer.
#define kIsVisible true
#define kNoGoAway false
#define kNoWindowStorage 0L
#define kFrontWindow ((WindowPtr) -1L)
Here’s the body of the test code:
main()
{
char* myString = "\pThe EYE"; /* string to display */
GrafPtr offscreen; /* our off-screen bitmap */
Rect ovalRect; /* used for example drawing */
Rect myWBounds; /* for creating window */
Rect OSRect; /* portRect and bounds for off-screen bitmap*/
WindowPtr myWindow;
InitToolbox(); /* exercise for the reader */
myWBounds = qd.screenBits.bounds; /* size of main screen */
InsetRect(&myWBounds, 50,50); /* make it fit better */
DestroyOffscreenBitMap(offscreen); /* dump the off-screen bitmap */
while (!Button()); /* give user a chance to see our work of art */
}
Comments
In the example code, the bits of the BitMap structure, which are pointed to by the baseAddr field, are allocated by a _NewPtr call. If your off-screen bitmap is close to the size of the screen, then the amount of memory needed for the bits can be quite large (on the order of 20K for the Macintosh SE or 128K for a large screen). This is quite a lot of memory to lock down in your heap and it can easily lead to fragmentation if you intend to keep the off-screen bitmap around for any length of time. One alternative that lessens this problem is to get the bits via _NewHandle so the Memory Manager can move them when necessary. To implement this approach, you need to keep the handle separate from the GrafPort (for example, in a structure that combines a GrafPort and a Handle). When you want to use the off-screen bitmap you would then lock the handle and put the dereferenced handle into the baseAddr field. When you are not using the off-screen bitmap you can then unlock it.
This example does not demonstrate one of the more typical uses of off-screen bitmaps, which is to preserve the contents of windows so that after a temporary window or dialog box obscures part of your windows and is then dismissed, you can quickly handle the resulting update events without recreating all of the intermediate drawing commands.
Make sure you only restore the pixels within the content regions of your own windows in case the temporary window partly obscures windows belonging to other applications or to the desktop. Another application could change the contents of its windows while they are behind your temporary window, so you cannot simply restore all the pixels that were behind the temporary window because that would restore the old contents of the other application’s windows. Instead, you could keep keep an off-screen bitmap for each of your windows and then restore them by copying each bit map into the corresponding window’s ports when they get their update events.
An alternate method is to make a single off-screen bitmap that is as large as the temporary window and a region that is the union of the content regions of your windows. Before you display the temporary window, copy the screen into the off-screen bit map using the region as a mask. After the temporary window is dismissed, restore the obscured area by copying from the off-screen bit map into a copy of the Window Manager port, and use the region as a mask. If the region has the proper shape and location, it prevents _CopyBits from drawing outside of the content regions of your windows. See Technical Note #194, WMgrPortability for details about drawing across windows.
In some cases it can be just as fast and convenient to simply define a picture (PICT) and then draw it into your window when necessary. There are cases, however, such as text rotation, where it is advantageous to do the drawing off the screen, manipulate the bit image, and then copy the result to the visible window (thus avoiding the dangers inherent in writing directly to the screen). In addition, this technique reduces flicker, because all of the drawing done off the screen appears on the screen at once.
It is also important to realize that, if you plan on using the pre-Color QuickDraw eight-color model, an off-screen bitmap loses any color information and you do not see your colors on a system that is capable of displaying them. In this case you should either use a PICT to save the drawing information or check for the presence of Color QuickDraw and, when it is present, use a PixMap instead of a BitMap and the color toolbox calls (Inside Macintosh, Volume V) instead of the standard QuickDraw calls (Inside Macintosh, Volume I).
You may also want to refer to the OffScreen library (DTS Sample Code #15) which provides both high- and low-level off-screen bitmap support for the 128K and later ROMs. The OffSample application (DTS Sample Code #16) demonstrates the use of this library.
Further Reference:
• Inside Macintosh, Volumes I & IV, QuickDraw
• Inside Macintosh, Volume V, Color QuickDraw
• Technical Note M.IM.PrincipiaOffscreen —
Principia Off-Screen Graphics Environments
• Technical Note M.TB.WMgrPort —
WMgrPortability
• DTS Macintosh Sample Code #15, OffScreen & #16, OffSample
QD 8 - Old-Style Colors
QuickDraw
Revised by: Bill Guschwan May 1993
Written by: Rich “I See Colors” Collyer and Byron Han October 1989
This Technical Note covers limitations of the original Macintosh color model (eight-color) that Inside Macintosh Volume I, page I-173, QuickDraw, does not document.
Changes since October 1989:
Added definitions of the old-style constants.
Corrected definitions of old-style constants to reflect MPW 3.2 interfaces.
QuickDraw has always been able to deal with color, just on a very limited basis. Most applications have not made use of this feature, since Color QuickDraw–based Macintosh computers come with a better color model. There are, however, a few nice features that come with the old-style color model. With the old-style colors, it is easy to print color on an ImageWriter with a color ribbon. Another advantage is that developers do not have to write special-case code depending on whether or not a machine has Color QuickDraw.
Now that you are ready to convert to the old-style colors, there are a few things you should know about that do not work with old-style colors. This Note covers the limitations of using old-style colors, as well as the best ways to work around these limitations.
Limitations
The most obvious limitation is that of only eight colors: black, white, red, green, blue, cyan, yellow, and magenta. This limitation is a problem only if you want to produce a color-intensive application; if this describes your application, then you need not read any further in this Note.
The next limitation is that off-screen buffers are not very useful. You can draw into off-screen buffers, but there is no way to get the colors back from the buffer. This leads into the next limitation, which is that _CopyBits cannot copy more than one color at a time.
When you call _CopyBits from an off-screen buffer to your window, you need to set the forecolor to the color you want to copy before calling _CopyBits (for example, to copy a red object, call _ForeColor(redColor)). Now when you copy the object, you can copy only one color. If you copy different colored objects at one time, then you have a problem. The result of a multicolored copy is that all objects copy in the same color, that of the foreground.
It is possible to work with an off-screen buffer and the old-style colors, but it requires a lot of extra work. Unless the objects are really complex, then it is probably easier to just draw the objects directly into your window.
One other limitation does exist. Consider the following code sample. One would assume that this sample would work at all times.
SetPort (myPort);
savedFG := myPort^.fgColor;
ForeColor (redColor); {or any other color}
{...drawing takes place here...}
ForeColor (savedFG);
Surprise. It does not always work. The saved value for the fgColor field of the grafPort is not a classic QuickDraw color if the grafPort is actually a cGrafPort. If dealing with a cGrafPort, the fgColor field actually contains the foreground color’s entry in the color table, so the second call to _ForeColor really messes things up.
The proper way to set and reset the foreground color with classic QuickDraw’s _ForeColor call is as follows:
SetPort (myPort);
savedFG := myPort^.fgColor;
ForeColor (redColor); {or any other color}
{...drawing takes place here...}
myPort^.fgColor := savedFG; {Manually stuff the old fgColor back.}
If (32BQD = TRUE) Then {32BQD is a flag that is made and set by}
PortChanged (myPort); {the application; to set it, the application}
{needs to check _Gestalt for 32-Bit QuickDraw.}
This Note also applies to the routine _BackColor.
What Works
The easiest way to work with these limited colors is to use pictures. When you draw the images, you should draw into a picture. Then when you want to draw the images into your window or to a printer, call _DrawPicture. Pictures work well with the old-style colors, and you don’t need to worry about making sure that the forecolor is current when you draw into your window.
Once you have the picture, you can use it to draw into the screen or to the printer port. You can also set the WindowRecords windowPic to equal your PictureHandle so updates are handled by the Window Manager.
What Do Those Constants Mean Anyway
The correct values are
blackColor = 33
whiteColor = 30
redColor = 205
greenColor = 341
blueColor = 409
cyanColor = 273
magentaColor= 137
yellowColor = 69
The following discussion is theoretical and was based on the color constants for the MPW 3.1 interfaces. Well, those interfaces were wrong as far as the color constants. The discussion will be kept here to prove once and for all that Macintosh programming sometimes is arbitrary and not logical. On the other hand, the information about the color bits is correct.
Each of the constants contains 9 bits of information, and each bit has a special meaning. Figure 1 illustrates the meaning of each of the bits, while Table 1 shows how each of the color constants fills in the appropriate bits.
Figure 1—Bit Definitions
Table 1 Color-Bit Correlation
black white red green blue cyan magenta yellow
(33) (30) (209) (329) (389) (269) (149) (89)
Cyan 0 0 0 1 1 1 0 0
Magenta 0 0 1 0 1 0 1 0
Yellow 0 0 1 1 0 0 0 1
Black 1 0 0 0 0 0 0 0
Red 0 1 1 0 0 0 1 1
Green 0 1 0 1 0 1 0 1
Blue 0 1 0 0 1 1 1 0
Inverse 0 1 0 0 0 0 0 0
Normal 1 0 1 1 1 1 1 1
Further Reference:
• Inside Macintosh, Volume I, page I-173, QuickDraw
• Technical Note M.IM.Copybits—Of Time and Space and _CopyBits
QD 9 - Palette Manager Changes in System 6.0.2
QuickDraw
Written by: Guillermo Ortiz October 1988
This Technical Note describes the changes and enhancements to the Palette Manager in System Software 6.0.2 and future versions.
Application Palette
Applications now have the ability to define a default palette for the system to use when it needs to define the color environment (i.e., when it creates a color window without an associated palette or displays a dialog box).
The application palette feature is especially cool in cases where a color application uses old-style dialogs and alerts because without an application palette, the system will use the default palette to define the color environment. Since the system uses the default palette, the color environment may change (will change in 16-color mode) to cause some “cosmic” colors to appear in the active window. Defining a default application palette with two colors, black and white, solves this problem.
If the system needs a palette to define a color environment, it looks in the resource fork of the application for the 'pltt' ID = 0 resource and uses the palette which it contains. If the system cannot find this resource in the application’s resource fork, it will use its own default palette (resource 'pltt' ID = 0 in the System file) if present, or, if necessary, it will use the Palette Manager’s built-in palette.
Once an application has set its color environment (by calling _InitMenus, or _InitPalettes in weird instances when there are no menus) it can find the default palette by calling GetPalette ( WindowPtr (-1) ) or change the default palette by calling SetPalette ( WindowPtr (-1), newDefPltt, true ). Note that the initialization of the Palette Manager with a call to _InitMenus is contrary to the way Inside Macintosh, Volume V, The Palette Manager documents it.
One Palette, Many Ports
You can now associate one palette with many CGrafPort and CWindow records, thus simplifying the use of a single palette with multiple ports and windows; System Software 6.0 and earlier require copies of the palette to use it with different windows.
Although this ability to associate one palette with multiple ports and windows will allow the use of calls like _PmForeColor and _PmBackColor, calling _ActivatePalette with an off-screen port does nothing, and as a result, calling it with an off-screen port will associate the palette with the port but will not cause any change in the color environment.
One important implication of this feature is that DisposeWindow (_DisposWindow) will not dispose of the associated palette automatically since it may be allocated to other ports or windows. The only exception to this behavior is when an application has used _GetNewCWindow to create the window, there is a 'pltt' resource with the same ID as the window, and the application has not called _GetPalette for the window.
Color Updates
System version 6.0.2 also introduces a new call, _NSetPalette, which complements _SetPalette. _NSetPalette has the same functionality as _SetPalette, but the CUpdates parameter has been modified from a Boolean to an Integer as follows:
_NSetPalette changes the palette associated with dstWindow to srcPalette. It also records whether the window wants to receive updates as a result of a change to its color environment. If you want dstWindow to be updated whenever its color environment changes, set nCUpdates to pmAllUpdates. If you are only interested in updates when dstWindow is the active window, set nCUpdates to pmFgUpdates. If you are only interested in updates when dstWindow is not the active window, set nCUpdates to pmBkUpdates.
_CopyPalette is a utility procedure that copies dstLength entries from the source palette into the destination palette; the copy begins at srcEntry and dstEntry, respectively. _CopyPalette will resize the destination palette when the number of entries after the copy is greater than the original.
_CopyPalette does not call _ActivatePalette, so the application is free to do a number of palette changes without causing a series of intermediate changes to the color environment; the application should call _ActivatePalette after completing all palette changes.
If either of the palette handles are NIL, _CopyPalette does nothing.
QD 10 - Picture Comments—The Real Deal
QuickDraw
Revised by: Joseph Maurer October 1992
Written by: Ginger Jernigan November 1986
Changes since March 1988: This Note (formerly titled “Optimizing for the LaserWriter—PicComments”) describes the picture comments defined and interpreted by the Apple printer drivers. Most of the picture comments are specific to PostScript, but we renamed the Note to emphasize that LaserWriter printers are not necessarily PostScript devices, and that QuickDraw printer drivers may implement their own picture comment handling. This Note has been completely rewritten and incorporates all additional insights gained during the last few years. We are also much more determined now to discourage the use of obsolete and problem-laden (although still supported) picture comments, and we carefully point out known problems or limitations of each comment.
Introduction
The QDProcs record (see Inside Macintosh Volume I, page 197) reflects the foundations of the architecture of QuickDraw. The commentProc field points to a procedure that processes picture comments, as included in a picture by means of the PicComment procedure (Inside Macintosh Volume I, page 189). This allows applications to include application-specific additional information in the pictures they create.
The QDProcs record also is the key to understanding how Macintosh printer drivers work. When the application calls PrOpenPage and draws into the printing grafPort, the printer driver collects the drawing commands by hooking into the QDProcs of the printing port. In particular, if an application calls the PicComment procedure while drawing into the printing grafPort, the printer driver gets a chance to capture and process the information contained in the kind and dataHandle parameters.
During the development of the original LaserWriter driver, it became obvious that applications should be able to take advantage of certain PostScript features that were not accessible through standard QuickDraw calls, such as rotated text and graphics, dashed lines, fractional line widths, and smoothed polygons. Also, certain applications needed a way to transmit their own native PostScript instructions to the printer. Picture comments seemed to be the ideal vehicle for providing these capabilities.
Unfortunately, there are conflicts with the device-independent nature of the Macintosh Printing Manager architecture. In this Note, we still want to document picture comments as completely and correctly as possible; and we want to tell you how to use the best of their features, while maintaining the important goal of device-independent printing and all- purpose PICTs. (This is why it has such a painful history!)
First, we give an overview of the picture comments as currently implemented by Apple’s printer drivers. This leads us immediately to the problem section “Cohabitation of QuickDraw and PostScript,” which also shows how to keep the QuickDraw and PostScript graphics states synchronized during printing. Finally, we discuss all the picture comments by subject, in the order suggested by Table 1.
Picture Comments Repertoire
The following picture comments are recognized by all PostScript LaserWriter drivers version 3.1 and later.
Table 1 PostScript LaserWriter Picture Comments
Data
Type Kind Size Data Description
TextBegin 150 6 TTxtPicRec Begin text function
TextEnd 151 0 NIL End text function
StringBegin 152 0 NIL Begin string delimitation
StringEnd 153 0 NIL End string delimitation TextCenter 154 8 TTxtCenter Offset to center of rotation
LineLayoutOff 155 0 NIL Turn LaserWriter line layout off
LineLayoutOn 156 0 NIL Turn LaserWriter line layout on
# ClientLineLayout 157 16 TClientLL Customize line layout error
distribution
PolyBegin 160 0 NIL Begin special polygon
PolyEnd 161 0 NIL End special polygon
PolyIgnore 163 0 NIL Ignore following polygon data
PolySmooth 164 1 PolyVerb Close, Fill, Frame
PolyClose 165 0 NIL Close the polygon
DashedLine 180 - TDashedLine Draw following lines as dashed
DashedStop 181 0 NIL End dashed lines
SetLineWidth 182 4 Point Set fractional line widths
PostScriptBegin 190 0 NIL Set driver state to PostScript
PostScriptEnd 191 0 NIL Restore QuickDraw state
PostScriptHandle 192 - PSData PostScript data in handle
† PostScriptFile 193 - FileName FileName in data handle
† TextIsPostScript 194 0 NIL QuickDraw text is sent as PostScript
† ResourcePS 195 8 Type/ID/Index PostScript data in a resource file
PSBeginNoSave 196 0 NIL Set driver state to PostScript
RotateCenter 202 8 Center Offset to center of rotation
# FormsPrinting 210 0 NIL Don’t clear print buffer after each page
# EndFormsPrinting 211 0 NIL End forms printing after PrClosePage
______________________________________
† These comments are obsolete.
# These comments are not recommended.
Most of the comments in Table 1 were designed specifically for the original LaserWriter driver. In fact, the term LaserWriter has been (and often still is) used in the sense of “PostScript printer,” and the LaserWriter driver is known to be basically a QuickDraw-to-PostScript translator. Meanwhile, however, QuickDraw-based LaserWriter models came out, so we should start being more careful in our terminology. This is why we insist on talking about PostScript drivers or PostScript printers when a picture comment applies to PostScript.
QuickDraw printer drivers may implement their own picture comments, or some of the above comments, in order to provide additional capabilities. Certain third-party printer drivers implement text rotation, for example, by supporting the TextBegin/TextCenter/ TextEnd picture comments.
Apple’s QuickDraw printer driver for the LaserWriter SC supports the following three picture comments:
LineLayoutOff 155 0 NIL Turn LaserWriter line layout off
LineLayoutOn 156 0 NIL Turn LaserWriter line layout on
SetLineWidth 182 4 Point Set fractional line widths.
The ImageWriter LQ driver and the first versions of the StyleWriter driver (prior to 7.2) implement the LineLayoutOff and LineLayoutOn picture comments. Even the ImageWriter driver reacts to picture comments:
BitMapThinningOff 1000 0 NIL Turn off hi-res bitmap thinning
BitMapThinningOn 1001 0 NIL Turn on hi-res bitmap thinning
The ImageWriter driver does the same toggling of the “bitmap thinning” of fat bitmaps in Best mode, when it encounters a TextBegin or TextEnd comment (undocumented feature—never mind!). The ImageWriter LQ driver handles these comments similarly.
The current StyleWriter driver (version 7.2.2) and the personal LaserWriter LS driver do not support any picture comments at all.
The point of all this is:
It is impossible to determine which picture comments
are supported by which printer driver.
In other words, your application should never assume a particular picture comment is available, but your application also should not defeat the device-independent design of the Macintosh printing architecture by writing printer driver–specific code!
Of course, you know (Inside Macintosh Volume II, page 152) that the high byte of the prStl .wDev field of the print record identifies a printer driver species, and that a value of $03 tells you the printer driver belongs to the PostScript LaserWriter driver ancestry. As a matter of fact, many applications use this information to achieve special printing features not available through the Printing Manager interface.
And, of course, you also know that we don’t like this idea. One reason is future system software may allow spool files to be redirected to a printer other than the one chosen when you sent your printing instructions (including picture comments). Another reason is that picture comments usually are included in PICTs; documents containing such pictures should print with optimal results on any printer configuration. And, finally, you never know what the future holds for you, in terms of new printing devices or new printer drivers—or a new printing architecture!
Instead, if a picture (i.e., a sequence of imaging instructions) contains picture comments to enhance the output on devices that support them, it should also contain a standard QuickDraw representation as a fallback solution, in case the rendering device does not recognize the picture comments. The design and implementation of these picture comments should incorporate conventions to make this cohabitation of two representations in one picture possible.
Cohabitation of QuickDraw and PostScript
Device-Independent Pictures
We can think of the Printing Manager’s PrOpenPage and PrClosePage calls as being analogous to the OpenPicture and ClosePicture calls (which, by the way, reminds us to never call OpenPicture between PrOpenPage and PrClosePage; see Inside Macintosh Volume II, page 160). In both cases, a stream of imaging instructions is recorded for deferred rendering. We want to create pictures that include both QuickDraw and optimized PostScript representations so that we obtain the best results in all circumstances. We must take special care for third-party QuickDraw printer drivers that support picture comments originally intended for PostScript devices only.
Let’s start with the easy ones.
The two picture comments PostScriptBegin and PostScriptEnd clearly suggest that any imaging instructions in between are intended exclusively for PostScript printing devices. In the case of the PostScript LaserWriter driver, the effect of PostScriptBegin is to disable all bottlenecks except commentProc, txMeasProc, getPicProc, and putPicProc. This means that QuickDraw’s text, line, shape (Rects, RoundRects, Ovals, Arcs, Polygons) and bitmap drawing calls don’t have any effect in the printing grafPort when enclosed by PostScriptBegin and PostScriptEnd. Instead, the PostScript LaserWriter driver expects to receive the imaging instructions as data enclosed in the PostScriptHandle comment. This way, both the PostScript and QuickDraw representation can coexist in the same picture without conflict. As a consequence, non-PostScript printer drivers, unable to interpret general PostScript text, must not imitate this behavior of ignoring QuickDraw instructions, even when they implement other picture comments such as TextBegin and TextEnd for text rotation. Otherwise, they would miss the QuickDraw representation of some PostScript imaging.
The text rotation picture comments (TextBegin, TextCenter, TextEnd) silently include the assumption that a printer driver that supports these comments
a. ignores the QuickDraw clipping region between TextBegin and TextEnd
b. ignores the QuickDraw CopyBits instruction within TextBegin/TextEnd
This way, a bitmap representation of the rotated text can be included in the picture. It will be used only if the TextBegin/TextEnd comments are not supported. Conversely, the QuickDraw commands required to draw the text to be rotated by the printer driver are “hidden” from QuickDraw by setting the clipping region to empty, which is ignored by the driver supporting the comments.
The polygon picture comments provide another solution to the problem, in form of the special comment PolyIgnore. It allows one to include a QuickDraw representation of the smoothed polygon, ignored by a driver that supports polygon smoothing (such as via PostScript’s curve operator). And for filled polygons, QuickDraw’s region concept works around a conflict of who owns which polygon (see sample code later in this Note).
Some picture comments (such as the line layout comments) do not require a fallback solution in case they are not supported by a printer driver; or the feature, if absent, is not a big loss (such as SetLineWidth, provided you use it only for widths smaller than one 72-dot-per-inch (dpi) QuickDraw pixel).
But for the picture comments DashedLine/DashedStop and RotateBegin/ RotateCenter/RotateEnd, there is no general solution to the “cohabitation” problem; and we are distressed about it. It is obvious that they have been defined with the PostScript LaserWriter driver in mind, without anticipating a future furnished by some 150 third-party printer drivers. The only way to include both representations in these cases is indeed to assume that only PostScript drivers will support the picture comment, such that the PostScriptBegin and PostScriptEnd comments can be used to “comment out” the QuickDraw representation.
Even under the above assumption, we still need a trick to prevent the QuickDraw calls within the scope of the picture comments from showing up when the comments are not recognized. Fortunately, early Macintosh developers discovered a QuickDraw feature that, unintentionally, solves the problem. Passing the “magic” mode 23 to PenMode inhibits QuickDraw’s normal drawing, but still lets the LaserWriter driver see the drawing instructions come through the bottlenecks, so that it can translate them into PostScript. Note that this pen mode always has been undocumented, and that using it was considered a compatibility risk and frowned upon for some time. Given the current state of affairs, however, there is no reason anymore to be paranoid about it.
Keeping QuickDraw and PostScript Synchronized
There are two situations, in the context of picture comments, where the design of the PostScript LaserWriter driver requires special precautions from the application programmer.
First, certain QuickDraw instructions like Move, MoveTo, PenPat, and PenSize change the state of the grafPort, without going through the QDProcs bottleneck procedures. A Macin-tosh printer driver takes these changes into account only at the time it executes an actual drawing instruction. Remember, the printer driver hooks into the QDProcs to get execution time and only “sees” instructions coming through the QDProcs. Nothing is wrong with it—unless PostScript code is woven into the graphics instructions by means of picture comments. (Note that PostScript code may be generated transparently when the LaserWriter driver encounters certain picture comments.) If the PostScript code assumes that the current state of the grafPort corresponds to what you expect it to be, then you have to flush the state of the grafPort explicitly before inserting the PostScript code. This is easier than it sounds; just do something inoffensive that goes through the QDProcs.lineProc bottleneck, like in the following utility procedure:
PROCEDURE FlushGrafPortState;
{ This routine causes the state of the Printing Manager's grafPort to be }
{ flushed out to the LaserWriter, by making a dummy call through the }
{ QDProcs.lineProc bottleneck. Pen size and pen location are preserved. }
Another unwanted effect is related to the PostScript LaserWriter driver’s multiple internal buffering of generated PostScript code. The PostScript code generated for text drawing instructions (which usually involves font queries and, sometimes, font downloading) is buffered independently from the PostScript code inserted by means of picture comments. In certain cases, this results in apparently nonsequential execution of drawing instructions, and may affect clipping regions or may have side effects on the PostScript code you included in picture comments. In order to synchronize the sequence of QuickDraw instructions with the generation of PostScript code, you need to call the following procedure:
PROCEDURE FlushPostScriptState;
{ This routine flushes the buffer maintained by the LaserWriter driver. }
{ All PostScript, generated either by the app or by the LaserWriter }
{ driver, will be sent to the device. }
BEGIN
PicComment(PostScriptBegin, 0, NIL);
PicComment(PostScriptEnd, 0, NIL);
END;
In the following discussion of picture comments, we’ll refer to these two utility routines as appropriate.
Text Rotation
Comments: TextBegin, TextCenter, TextEnd
These comments give access to PostScript’s capabilities of rotating, flipping, and justifying text. They are intended for applications likely to be used with PostScript printers (such as desktop publishing and advanced drawing applications), but which don’t want to use PostScript explicitly. Note that some non-PostScript printer drivers support these comments as well. For situations where the comments are not supported (such as the QuickDraw screen, or most QuickDraw printer drivers), you must provide a bitmap represen-tation of the rotated text as an alternative.
Let’s look at sample code right away.
USES PicComments; { See Appendix; defines constants for just and flip and }
{ the structures referred to by TTxtPicHdl and TCenterHdl.}
PROCEDURE QDStringRotation(s: Str255; ctr: Point;
just, flip: Integer; rot: Fixed); EXTERNAL;
{ This routine should generate a bitmap of the rotated and flipped text }
{ and use CopyBits to draw it to the grafPort. Left as an exercise ... }
{ PostScript graphics state now has rotated/flipped coordinates. }
oldClip := NewRgn;
GetClip(oldClip);
SetRect(zeroRect,0,0,0,0);
ClipRect(zeroRect); { Hides the following DrawString from QuickDraw }
DrawString(s); { in the rotated PostScript environment. }
ClipRect(oldClip^^.rgnBBox);
{ Now the "fallback" bitmap representation; see the comments above }
{ at the declaration of the QDStringRotation procedure. }
QDStringRotation(s, ctr, just, flip, rot);
PicComment(TextEnd,0,NIL); { Set environment back to the original state }
DisposHandle(Handle(hT));
DisposHandle(Handle(hC));
MoveTo(pt.h,pt.v); { to preserve the pen position }
END;
The preceding discussion about including both QuickDraw and PostScript representations and the comments included in the source code say it all: The conventions tied to the usage of the TextBegin and TextEnd picture comments allow you to take advantage of a printer driver’s implementation of high-resolution text rotation, while including a bitmapped representation for where the comments are not supported.
Some Additional Hints
• Because of QuickDraw’s orientation of the vertical coordinate axis, the rotation angle is measured clockwise. Nothing prevents us from using the negative angle if we are used to the counterclockwise orientation.
• The angle is measured in degrees (0..360), and passed as a Fixed type number (that is, if taken as a LongInt value, you have to divide it by 65536 to obtain the angle in degrees). For integer angles, it is possible to use a reduced TTxtPicRec structure that does not contain the tRotFrac field. The PostScript LaserWriter driver uses GetHandleSize(hT) to determine whether it must use the fractional angle in the tRotFrac field. To be safe, always set the tRot field to FixRound(tRotFrac) if you go with the extended TTxtPicRec (as we do here).
• It is convenient that clipping regions are ignored between the TextBegin and TextEnd picture comments, because it allows us to clip out the DrawString on printers that don’t support these comments. Unfortunately, this also means that text rotated this way can’t be clipped. If clipping of rotated text is required, you’ll have to do it entirely within PostScript.
• Due to the LaserWriter driver’s internal buffering of generated PostScript code, the effect of ignoring clip regions may be propagated to preceding sections of your drawing instructions. We recommend calling the FlushPostScriptState procedure described earlier immediately before the TextBegin comment.
• The tJus field in the TTxtPicRec, if different from tJusNone, tells the printer driver to maintain either the left, right, or center point of the string without recalculating the interword and intercharacter spacing. The tJusFull value specifies that the original length of the string (on the QuickDraw screen) must be maintained. This is important when the printer font has widths different from those of the screen font, and when you rotate justified blocks of text.
• The tFlip field in the TTxtPicRec specifies horizontal or vertical flipping about the center point specified by the TextCenter comment.
• The TextCenter comment specifies the center of rotation for any text enclosed within the TextBegin and TextEnd calls, as an offset to the location of the current point. The rotation is achieved by changing PostScript’s coordinate system. A sequence of DrawString – MoveTo instructions is rotated as a whole until TextEnd is encountered.
• Some versions of double-byte Kanji systems print Kanji characters by calling CopyBits instead of calling standard text drawing routines. This means the comments in the Text Rotation family cannot be used with these fonts. Instead, use the Graphics Rotation comment family described later in this Note.
When drawing to a printing grafPort, the selected printer driver does a lot of work “behind the scenes” to try to maintain the infamous “What-You-See-Is-What-You-Get” (WYSIWYG) metaphor from the screen to the paper, and generally to make the printed output look as good as possible. Depending on the target device, the printer driver, and the configuration of fonts in the system, the font you draw text with may be scaled, smoothed, remapped, or even replaced by a font built into the printer. In nearly all cases where the device resolution of the printer is different from QuickDraw’s “hard-coded” 72-dpi screen resolution, the width of text rendered on the printer is different from the text width on the screen. This is due to nonproportionally scaling bitmapped fonts, different character widths after font substitution, and rounding errors of fractional character widths on the screen. The difference in the width of a line of text is called the line layout error.
The printer driver is responsible for adjusting the word and character spacing in the printed output so that the two widths are identical. If it doesn’t, apparently fully justified text on the screen may appear ragged on the paper, and certain lines of text may extend beyond the right border and be badly clipped. Many existing applications make this task really difficult for the printer drivers (don’t blame them, though!). They position the words (or even characters) separately on a line, and the printer driver has to figure out how to collect the complete line before applying its line layout algorithm to distribute the difference of the text widths into word and character spacing. Given the uneven distribution of the character width differences, and the requirement of achieving good typographical quality in the printed output, it is unavoidable that the position and width of a word within a justified line differs slightly from what appears on the screen; only the length of the whole line is maintained.
In computing the required line layout adjustments, the LaserWriter driver proceeds as follows:
1. It collects text coming through the printing grafPort’s textProc bottleneck, and heuristically puts it together into what it “believes” is a logically contiguous line of text. This includes text moved vertically away from the baseline, to take care of indices or exponents in the text. The process of accumulating text is stopped when the LaserWriter driver detects that the horizontal component of the pen position has changed since the previous text drawing instruction, or when picture comments like TextBegin, TextEnd, StringBegin, StringEnd are encountered.
2. It determines the width of the accumulated logical line of text, both on the screen and on the printer, and distributes the line layout error among the interword and intercharacter spacing of the printed output.
The LineLayoutOff picture comment disables only the second step (distribution of the line layout error); the heuristic algorithm of accumulating text into a logically contiguous piece is not affected. Otherwise, if the character widths of the printer font are different from those of the screen font, and if the text contains exponents or indices, the latter would often be misplaced.
The following code fragment shows a probably unexpected consequence of this behavior. We draw a line in two pieces three times. A vertical line shows the starting pen position of the second DrawString call. The second line is enclosed by LineLayoutOff and LineLayoutOn picture comments.
PROCEDURE ObserveLineLayout;
CONST
testString1 = 'Whatever you like, preferably ';
testString2 = 'with spaces, long and short words';
fontName = 'New York';
fontSize = 14;
x0 = 20; { starting point }
y0 = 40;
h = 30; { line height }
VAR
familyID: Integer;
w, y : Integer;
BEGIN
GetFNum(fontName, familyID);
TextFont(familyID);
TextSize(fontSize);
w := StringWidth(testString1);
y := y0;
MoveTo(x0 + w, y - h);
Line(0, 4 * h); { This is to estimate the difference. }
{ Draw the first line, in two pieces. }
{ This is the default line layout behavior of the LaserWriter driver. }
MoveTo(x0, y);
DrawString(testString1);
MoveTo(x0 + w, y);
DrawString(testString2);
{ Draw the second line, in the same way as above. }
{ Because of the LineLayoutOff picture comment, the unmodified widths }
{ of the printer font are used. }
y := y + h;
PicComment(LineLayoutOff, 0, NIL);
{ ••• (1) •••}
MoveTo(x0, y);
DrawString(testString1);
MoveTo(x0 + w, y);
{ ••• (2) •••}
DrawString(testString2);
y := y + h;
PicComment(LineLayoutOn, 0, NIL);
{ Back to the original behavior. }
MoveTo(x0, y);
DrawString(testString1);
MoveTo(x0 + w, y);
DrawString(testString2);
END;
And this is (approximately) the output of the ObserveLineLayout (with LaserWriter driver version 7.1.1, and the default setting “Font Substitution enabled”):
Figure 1—Effect of the LineLayoutOff comment
For most noticeable effects, we choose the bitmapped New York font, such that the LaserWriter driver substitutes PostScript Times (note that there are no line layout problems with TrueType fonts, unless the TrueType font has the same name and different cha-rac-ter widths as a printer-resident PostScript font). The screen font New York is larger than the PostScript font Times, and in the first and third lines, the printer driver (after accumulating testString1 and testString2 into one logical line) distributes the line layout error (mainly) among the spaces between words. You may even notice that the starting point of testString2 (“with ...”) has been slightly moved to the left in the process. The width of the whole line, however, is the same as on the screen.
The second line, where the LineLayoutOff comment is active, demonstrates a dramatic counterexample to the popular belief that this picture comment is here to assure precise positioning of text. It seems the opposite is true, and the LaserWriter driver has deliberately ignored the MoveTo(x0+w,y) instruction! What we would have expected is this:
Figure 2—Desired result of the LineLayoutOff comment
The attentive reader already knows the explanation. As mentioned earlier, we must break the LaserWriter driver’s heuristic line accumulation algorithm before drawing testString2. Short of adequate documentation, developers have found out that a FlushGrafPortState call right after the MoveTo(x0+w,y) instruction has the desired effect (see {••• (2) •••} in the code snippet given earlier). Unfortunately, it creates quite a lot of overhead in the pictures, and penalizes all printer drivers that don’t need it. A better solution is to use the StringBegin and StringEnd picture comments at the markers {••• (1) •••} and {••• (2) •••} in the code shown earlier. This indicates that testString1 is to be considered a logically independent text entity, and must not be put together with any other pieces of text coming through the textProc bottleneck. The overhead of these comments is much smaller, and they don’t affect other printer drivers at all.
The ClientLineLayout picture comment, supported by the (PostScript) LaserWriter driver, has never been documented. Its effect is rather subtle and very specific to the PostScript LaserWriter driver. Basically, it allows the application to redefine the character that absorbs the major part of the line layout error (usually the space character), and the percentages of the “major” and “minor” parts of the line layout error (usually 80 percent versus 20 percent). The “minor” part is distributed across intercharacter spacing.
Only very ambitious page layout applications might be interested in this functio-na-lity; but then, they should rather aim at a more general scheme of line layout control that does not rely upon this very driver-specific picture comment.
The PicComment.p interface (see the Appendix) describes the TClientLLRecord structure passed through the handle parameter to the picture comment. If you want, feel free to experiment with it; we recommend, however, that you do not use this picture comment in your application.
Caveats
• Some older printer drivers supporting the LineLayoutOff picture comment are unable to correctly obey a subsequent LineLayoutOn picture comment.
• Don’t forget that if you use LineLayoutOff, the burden of “WYSIWYG” is now on your shoulders, and not the printer driver’s.
• A previous version of this Note said that setting the Font Manager’s FractEnable global to TRUE implied the effect of the LineLayoutOff picture comment. As it turned out, the statement was based on observations with a specific (older) version of the LaserWriter driver, and is not true in general. The setting of FractEnable does have some more or less subtle effects on the line layout algorithm, however; and this is quite plausible. Similarly, the results of combining the picture comments LineLayoutOff and LineLayoutOn with calls to SpaceExtra (Inside Macintosh Volume I, page 172) or CharExtra (Inside Macintosh Volume V, page 77) are sometimes unpredictable, depending on the particular printer driver.
And Finally the Good News
Given that the effect of the LineLayoutOff and LineLayoutOn comments does not require any changes in your printing code, you don’t have to worry whether or not a particular driver supports them. They are useful mainly when you’re sure you want no external assistance in computing word and character spacing for full justification, or when you need precise control over the horizontal placement of words and characters (such as in forms or tabulated text) and understand how to achieve this.
String Delimitation
Comments: StringBegin, StringEnd
These comments allow applications to specify the logical beginning and end of a string, possibly drawn with multiple calls to a QuickDraw text drawing routine (such as DrawChar). If this was their only raison d’être, they would have no relationship with the PostScript LaserWriter driver. But, as already let out in the preceding section on line layout, they are important to notify the printer driver that it should consider the text coming through the textProc bottleneck between StringBegin and StringEnd as an independent entity. Otherwise, the driver might continue to perform its heuristic accumulation of text drawing instructions for the same line, and defeat your text positioning intentions. Indeed, both StringBegin and StringEnd trigger the generation of PostScript instructions for drawing the text that has been accumulated in a line layout buffer, and reinitialize the internal variables for line layout computations. In other words, you need these picture comments to turn the LaserWriter driver’s line layout behavior completely off.
PostScript has the built-in capability of drawing cubic Bézier curve sections (see the PostScript Language Reference Manual, Second Edition, page 393). This is convenient for “smoothing” of polygons. The polygon-related picture comments have been provided to give applications easy access to this PostScript feature, with provision for including a QuickDraw approximation of the curve.
Schematically, the polygon comments are used as follows:
PolyBeginComment; { Put the PostScript driver into “polygon mode.” }
ClipRect(zeroRect); { Hide the following from QuickDraw. }
PolyCloseComment; { Optionally, if “closed” smoothing desired. }
PolySmoothComment; { Tell the driver to draw a Bézier curve. }
DrawPolygon; { Invisible for QuickDraw; PostScript output = curve. }
PolyIgnoreComment; { The driver will ignore the following line drawing. }
SetClip(origClipRgn); { Make it visible for QuickDraw. }
DrawQDPolygon; { Usually, a QuickDraw approximation of the curve. }
PolyEndComment; { PostScript driver resumes standard mode. }
A piece of sample code is sometimes worth more than one or two pictures; below, you’ll find both. For clarity and completeness of the exposition, we provide the coordinate definition of the polygons through arrays of Points, initialized in a preliminary DefineVertices procedure. You can enclose the PolygonDemo procedure between OpenPicture and ClosePicture calls to create a picture containing both QuickDraw and PostScript representations (see Figures 3 and 4), or you can use it as is when a printing page is open.
USES PicComments;
{ See Appendix of this Note for the definition of the TPolyRec structure. }
CONST
kN = 4; { number of vertices for PostScript }
kM = 6; { number of vertices for QuickDraw approximation }
TYPE
PointArray = array[0..0] of Point; { range checking OFF }
PointArrayPtr = ^PointArray;
PROCEDURE DefineVertices(VAR p,q: PointArrayPtr);
CONST
cx = 280;
cy = 280;
r0 = 200;
BEGIN
{ The array p^ contains the control points for the Bézier curve: }
SetPt(p^[0],cx + r0,cy);
SetPt(p^[1],cx,cy + r0);
SetPt(p^[2],cx - r0,cy);
SetPt(p^[3],cx,cy - r0);
p^[4] := p^[0];
{ q^ contains the points for a crude polygon approximation of the curve: }
q^[0] := p^[0];
SetPt(q^[1],cx,cy + round(0.7 * (p^[1].v - cy)));
SetPt(q^[2],(p^[1].h + p^[2].h) DIV 2,(p^[1].v + p^[2].v) DIV 2);
SetPt(q^[3],cx + round(0.8 * (p^[2].h - cx)),cy);
SetPt(q^[4],q^[2].h,cy + cy - q^[2].v);
SetPt(q^[5],q^[1].h,cy + cy - q^[1].v);
q^[6] := q^[0];
END;
PROCEDURE PolygonDemo;
VAR
p,q: PointArrayPtr;
aPolyVerbH: TPolyVerbHdl;
i: Integer;
clipRgn, polyRgn: RgnHandle;
zeroRect: Rect;
BEGIN
p := PointArrayPtr(NewPtr(SizeOf(Point) * (kN + 1)));
1. The fPolyFrame and fPolyFill fields of the TPolyRec record are self-explanatory. The fPolyClose flag is redundant with the PolyClose picture comment, but is included for the convenience of the LaserWriter driver. It is often misunderstood. It does not mean the polygon is being closed automatically, such as with the PostScript closepath operator; instead, it affects the shape of the smooth curve. Figure 4 shows the result for fPolyClose = FALSE; the start and end point of the polygon is distinguished. In the case of fPolyClose = TRUE, all vertices of the polygon are treated in the same manner, and the resulting curve resembles a circle (in this case).
2. The anonymous fields f3..f7 are reserved and should be set to zero (that is, FALSE).
3. The polygon is drawn at the current pen location when the PolyBegin comment is received.
4. In general (and in this example), you do not need to open a region, collect the line segments in the region, and draw the polygon through FrameRgn. It is demonstrated here only to prepare you for situations where you want to fill the polygon with a pattern. You cannot open a polygon and use FillPoly, because the PostScript driver “owns” the polygon concept at this point and captures—and ignores—all line drawing between the PolyIgnore and PolyEnd comment. Regions do not interfere with polygons, however, and can be used to paint or fill the polygonal shape.
Caveats
PostScript Level 1 has problems with very large polygons (more than about 1000 points). The workaround is to subdivide the large polygon into several smaller ones.
You cannot combine the polygon picture comments with other comments such as the rotation comments or the DashedLine comment. It’s just another limitation—no comment.
Dashed Lines
Comments: DashedLine, DashedStop
PostScript allows applications to draw precisely dashed lines with a given dash pattern in every direction (see the setdash operator, PostScript Language Reference Manual, Second Edition, page 500). The QuickDraw ersatz of setting the pen pattern appears to be awkward at best; the result depends very much on the direction of the line. Coding correctly dashed lines in QuickDraw is quite a hassle and rather clumsy. This is why the DashedLine and DashedStop picture comments have been provided for applications where dashed lines are important and used frequently. Applications can take advantage of these comments when printing to a PostScript printer.
The DashedLine comment tells the driver that the line drawing instructions following the comment should be dashed according to the parameters in the TDashedLine structure (see the Appendix). These parameters closely correspond to the parameters passed to the PostScript setdash operator. Only the centered field of the TDashedLine structure is not currently supported by the LaserWriter driver. It should be set to 0 in case support for centering is added in the future.
Unlike the picture comments for text rotation or even polygon smoothing, the DashedLine picture comment should not be supported by a non-PostScript driver. The only way to include representations of dashed lines with and without usage of the DashedLine picture comment is to make the following assumption: If the DashedLine comment is supported, then the printer is a PostScript printer, and the PostScriptBegin/PostScriptEnd bracket may be used to hide the QuickDraw imaging from the printer. Remember that non-PostScript printer drivers must not ignore QuickDraw imaging within PostScriptBegin and PostScriptEnd!
But we still need a trick to hide the line drawing instructions within the DashedLine – DashedStop bracket from QuickDraw. Here comes the “magic pen mode” to our rescue:
PROCEDURE DashDemo;
CONST
magicPen = 23; { the infamous penMode ! }
cx = 280;
cy = 280;
r0 = 200;
VAR
dashHdl: TDashedLineHdl;
i: Integer;
a, rad : Extended;
BEGIN
PenSize(2,2);
{ First the PostScript picture comment version. }
{ The "magic pen mode" 23 makes the line drawing invisible for QuickDraw. }
{ Now, the QuickDraw version. The PostScript driver must ignore it, }
{ so we enclose it between PostScriptBegin and PostScriptEnd comments.}
PicComment(PostScriptBegin, 0, NIL);
PenSize(2,2);
FOR i := 0 TO 9 DO BEGIN
MoveTo(cx,cy);
DashedQDLine(round(r0 * cos(i * 20 * rad)),
- round(r0 * sin(i * 20 * rad)), dashHdl);
END;
PicComment(PostScriptEnd,0,NIL);
END;
By the way: The DashedQDLine procedure is intentionally missing. It’s not precisely the subject of this Note, and thus, again, is left as a spare-time exercise for the reader.
Caveat
As mentioned earlier, the current version of the PostScript LaserWriter driver produces poor results when the DashedLine picture comment is applied to polygons. Just don’t do it!
Fractional Line Width
Comment: SetLineWidth
QuickDraw’s design is based on a fixed 72-dpi resolution. Even when printing to a high-resolution device, the Printing Manager presents the printing grafPort, corresponding to the printable area of the page, in the integer-valued QuickDraw coordinate system with 72 dpi. Applications can use PrGeneral to image at higher device resolutions (see Inside Macintosh Volume V, page 410), but this is useful mainly for immediate printing. As a consequence, lines are usually always at least 1/72 inch wide, corresponding to the smallest pen size (1,1). For a 300-dpi device like the LaserWriter, this is disappointing.
The SetLineWidth comment allows an application to set the width of a line to any fractional value. A value of 1/4 approximately corresponds to a “hairline” on a 300 dpi LaserWriter. Curiously (but conveniently), a QuickDraw Point structure is passed in the PicComment’s data handle, the vertical coordinate representing the denominator, and the horizontal coordinate the numerator of the fraction.
Unfortunately, it is not implemented in all high-resolution QuickDraw printers; and if it is (as in the LaserWriter SC), it works differently than in PostScript printer drivers. Moreover, there is no possibility to include alternative imaging instructions in case SetLineWidth is not supported. While this is not much of a loss for hairlines, it prevents us from using the comment for fractional widths > 1, where the alternative would be to include a PenSize call with rounded arguments. Another drawback may be that, allegedly, there are plotter drivers out there that abuse this comment to set the pen color—clearly an unpleasant situation.
The difference in the implementation of the SetLineWidth comment between the PostScript LaserWriter driver and the LaserWriter SC appears as soon as SetLineWidth is used for the second time. The PostScript driver keeps an internal line scaling factor; this factor is initialized to 1.0 when a job is started. Each number passed through SetLineWidth is multiplied by the current internal scaling factor to get the effective scaling factor for the pen size. The LaserWriter SC driver, on the other hand, replaces its current scaling factor for the pen size completely by the new value passed through SetLineWidth. In order to support both implementations, you must always use an additional SetLineWidth step in order to reset the PostScript driver line width to 1.0, before scaling to the new value.
Example
Let’s say you have set the line width to 0.25, and want to replace it by a line width of 0.5. The following two SetLineWidth comments will have the desired effect on both PostScript (PS) and QuickDraw (QD) drivers that implement the SetLineWidth comment. You don’t care about the temporary line width of 4.0 on the QuickDraw driver.
Current Line Width Parameter Passed New Line Width
PS driver QD driver in SetLineWidth PS Driver QD Driver
0.25 0.25 4/1 1.0 4.0
1.0 4.0 1/2 0.5 0.5
The following sample code gives the expected results only on a PostScript LaserWriter and on QuickDraw printer drivers that have the SetLineWidth comment implemented.
oldWidth,newWidth: TLineWidth; { actually a "Point" }
i,j,y: Integer;
BEGIN
PenNormal;
y := y0;
SetPt(oldWidth,1,1); { initial linewidth = 1.0 }
FOR i := 1 TO 5 DO BEGIN
SetPt(newWidth,4,i);
{ want to set it to i/4 = 0.25, 0.50, 0.75 ... }
SetNewLineWidth(oldWidth,newWidth);
MoveTo(x0, y);
Line(d0, 0);
y := y + e0;
oldWidth := newWidth;
END;
END;
•A Slight Imperfection
•
•If you experiment with the above code and draw a whole series of hairlines, you will see (depending on the values of e0 and kN) that certain lines appear thicker than they should be. This is due to rasterization effects in PostScript’s scan conversion algorithm when the line width is close to the device pixel size. In many cases, the PostScript LaserWriter driver tries to compensate for this by rounding coordinates to the 300-dpi grid. If you include SetLineWidth (or, by the way, DashedLine) picture comments, however, this does not work. PostScript Level 2 addresses this problem by means of an optional stroke adjustment feature (see the PostScript Language Reference Manual, Second Edition, pages 322 and 515).
Graphics Rotation
Comments: RotateBegin, RotateCenter, RotateEnd
Like the picture comments discussed earlier in this Note in the section “Text Rotation,” the graphics rotation picture comments provide a method of rotating QuickDraw objects on PostScript devices. Instead of having QuickDraw perform the rotation, the printer driver rotates the entire PostScript coordinate space so that everything drawn between RotateBegin and RotateEnd will be rotated on the printer itself. This includes text drawing! You specify the center of rotation with RotateCenter and the angle of the rotation, together possibly with horizontal or vertical flipping, through the TRotation record (see the interface definitions in the Appendix).
Unlike text rotation, you must insert the RotateCenter comment and pass the relative offset to the center of rotation before you use the RotateBegin picture comment. The point passed to RotateCenter specifies the offset from the anchor point of the first object drawn after RotateBegin to the desired center of rotation. Once you set up the rotation parameters with RotateCenter and RotateBegin, you can draw the graphics objects you want to rotate.
Bad news: In order to include a QuickDraw representation of the rotated objects in case the rotation comments are not supported, we have to assume (again) that only PostScript drivers implement these comments. The only way to hide the QuickDraw substitute from the driver is to surround it by PostScriptBegin and PostScriptEnd comments; and, similarly to the DashedLine comment, we need to use the “magic pen mode” (23) to hide the unrotated drawing between RotateBegin and RotateEnd from QuickDraw. The following sample demonstrates this:
The PostScript comments tell the picture interpreter (usually the LaserWriter driver) that the application is going to communicate with the LaserWriter directly using PostScript code instead of QuickDraw. All QuickDraw drawing instructions between the PostScriptBegin and PostScriptEnd picture comments are ignored. The driver sends the PostScript text contained in the PostScriptHandle data to the printer with no preprocessing and no error checking. When the application is finished sending PostScript, the PostScriptEnd comment tells the printer driver to resume normal QuickDraw mode. The driver uses the PostScript save and restore operators to preserve the state of the PostScript interpreter across the section enclosed by PostScriptBegin and PostScriptEnd. Some applications do not want to restore the previous state of the PostScript interpreter after including their PostScript code; for these situations, the PSBeginNoSave comment is a replacement for PostScriptBegin that does not preserve the state. Clearly, this comment should be used with extreme caution.
Some state information may be stored in global variables, so nesting PostScriptBegin (or PSBeginNoSave) and PostScriptEnd comments is not allowed.
The PostScriptHandle comment gives developers direct access to PostScript from applications. Instead of having the LaserWriter driver convert QuickDraw calls into the corresponding PostScript code, the application can generate its own PostScript, and transmit it to the printer or include it in a picture through the data handle of the PicComment procedure. The handle contains pure ASCII text; the valid length of the data is specified in the PicComment’s size parameter. Don’t forget to terminate the PostScript text at least with a space character, or better with a carriage return (ASCII $0D), so that it is separated from the following PostScript instructions (either yours, or the printer driver’s).
You must still use PostScriptBegin (or PSBeginNoSave) and PostScriptEnd around PostScriptHandle comments or the LaserWriter driver will not properly save and restore the PostScript drawing environment.
As with all picture comments, the handle you pass belongs to you and you must dispose of it when you’re finished with it.
PROCEDURE PostScriptLine(s: Str255);
{ A utility procedure to transmit a string of PostScript code through }
{ the PostScriptHandle picture comment to the PostScript printer. }
{ It should be called only between PostScriptBegin and PostScriptEnd }
{ picture comments. }
VAR
h: Handle;
BEGIN
h := NewHandle(256);
IF h = NIL THEN DebugStr('NewHandle failed');
BlockMove(@s[1],h^, Length(s));
PicComment(PostScriptHandle, Length(s), h);
h^^ := 13;
PicComment(PostScriptHandle, 1, h); { add a carriage return }
{ Let's test to see if the definition from above is still avail-}
{ able. This assumes that no font downloading has occurred. }
PicComment(PostScriptBegin,0,NIL);
PostScriptLine('//userdict /myFrameRect get exec ');
PicComment(PostScriptEnd,0,NIL);
END;
Caveat
If you choose to use PostScript directly in your pictures, be very careful not to make assumptions about Apple’s "md" dictionary (essentially the contents of the former LaserPrep file). Otherwise, your pictures will not print correctly with future versions of the PostScript LaserWriter driver. Also, be aware of compatibility problems within the PostScript world, and watch out for printers with PostScript Level 1 and PostScript Level 2 interpreters, and “PostScript-compatible” printers (PostScript clones).
FormsPrinting Picture Comments
Comments: FormsPrinting, EndFormsPrinting
The FormsPrinting comment tells the PostScript LaserWriter driver not to clear its page buffer after printing a page. EndFormsPrinting turns this mode off. When the page is completed, the application must erase the areas that need to be updated and draw the new information. The graphics that make up the form are drawn only once per page, which may improve performance. Currently, you need to write special printing code for the PostScript LaserWriter driver if you want to use this comment.
(More or Less) Obsolete PostScript Picture Comments
Comments: SetGrayLevel,
TextIsPostScript, ResourcePS, PostScriptFile
The SetGrayLevel picture comment was designed to provide access to the PostScript setgray operator while still drawing with QuickDraw in black-and-white mode. In practice, this turned out to be not so useful, however. For most drawing operations, the printer driver sets the gray level to match the foreground color currently stored in the printing grafPort, and the effect of the SetGrayLevel comment is often unpredictable. If direct access to the PostScript setgray operator seems nevertheless desirable, it is easy to include the instruction in a PostScriptHandle comment.
The TextIsPostScript picture comment takes all the text coming through standard QuickDraw text drawing calls (DrawChar, DrawString, DrawText, and anything else that eventually calls the StdText bottleneck), and interprets it as a PostScript program. There is no good reason to use this picture comment, but there is one important reason not to use it: Printer drivers that do not deal with the TextIsPostScript comment will print the PostScript text instead of interpreting it! If you need to transmit pure PostScript code directly to a printer that understands it, use the PostScriptHandle comment, and include a QuickDraw representation for all other printer drivers.
The ResourcePS picture comment loads PostScript code from a specified resource. The resource file is expected to be open at the time that the ResourcePS comment is used. Under background printing, there are no guarantees the file will still be open when the Printing Manager needs it. For this reason alone, you should forget about this comment. If you want to keep PostScript instructions in a resource, it is easy to write a small routine that loads the resources and sends their contents using the PostScriptHandle comment described earlier in this Note.
PostScriptFile has the same problems as ResourcePS described above. Basically, the Printing Manager cannot guarantee that the file will be available when it’s needed.
Appendix: Pascal Interface for Picture Comments
(File PicComments.p)
CONST
TextBegin = 150;
TextEnd = 151;
StringBegin = 152;
StringEnd = 153;
TextCenter = 154;
LineLayoutOff = 155;
LineLayoutOn = 156;
ClientLineLayout = 157;
PolyBegin = 160;
PolyEnd = 161;
PolyIgnore = 163;
PolySmooth = 164;
PolyClose = 165;
DashedLine = 180;
DashedStop = 181;
SetLineWidth = 182;
PostScriptBegin = 190;
PostScriptEnd = 191;
PostScriptHandle = 192;
PostScriptFile = 193;
TextIsPostScript = 194;
ResourcePS = 195;
PSBeginNoSave = 196;
SetGrayLevel = 197;
RotateBegin = 200;
RotateEnd = 201;
RotateCenter = 202;
FormsPrinting = 210;
EndFormsPrinting = 211;
tJusNone = 0; { values for the tJus field of the TTxtPicRec record }
tJusLeft = 1;
tJusCenter = 2;
tJusRight = 3;
tJusFull = 4;
tFlipNone = 0; { values for the tFlip field of the TTxtPicRec record }
tFlipHorizontal = 1;
tFlipVertical = 2;
TYPE
TTxtPicHdl = ^TTxtPicPtr;
TTxtPicPtr = ^TTxtPicRec;
TTxtPicRec = PACKED RECORD
tJus : Byte;
tFlip : Byte;
tAngle: Integer; { clockwise rotation in degrees 0..360 }
tLine : Byte; { Unused/Ignored }
tCmnt : Byte; { reserved }
tAngleFixed: Fixed; { same as "tAngle" in Fixed precision }
END; { TTxtPicRec }
TRotationHdl = ^TRotationPtr;
TRotationPtr = ^TRotation;
TRotationRec = RECORD
rFlip: Integer;
rAngle: Integer; { clockwise rotation in degrees 0..360 }
rAngleFixed: Fixed; { same as "rAngle" in Fixed precision }
END; { TRotationRec }
TCenterHdl = ^TCenterPtr;
TCenterPtr = ^TCenter;
TCenterRec = RECORD {offset from current pen location to center of rotation}
y: Fixed;
x: Fixed;
END; { TCenterRec }
TPolyVerbHdl = ^TPolyVerbPtr;
TPolyVerbPtr = ^TPolyVerbRec;
TPolyVerbRec = PACKED RECORD
f7,f6,f5,f4, f3, { reserved }
fPolyClose, { TRUE = smoothing across endpoint.}
fPolyFill, { TRUE = Polygon should be filled. }
fPolyFrame: BOOLEAN; { TRUE = Polygon should be framed. }
END;
TDashedLineHdl = ^TDashedLinePtr;
TDashedLinePtr = ^TDashedLineRec;
TDashedLineRec = PACKED RECORD
offset : SignedByte; { offset into pattern for } { first dash }
centered : SignedByte; { (Ignored) }
intervals: ARRAY [0..5] { Array of dash intervals }
OF SignedByte; { intervals[0] = number }
END; { of interval specs. }
TLineWidthHdl = ^TLineWidthPtr;
TLineWidthPtr = ^TLineWidth;
TLineWidth = Point; { v = numerator, h = denominator. }
TClientLLHdl = ^TClientLLPtr; { used in the ClientLineLayout picture comment }
TClientLLPtr = ^TClientLLRec;
TClientLLRec = RECORD
chCount : Integer; { Apply for so many characters. }
major : Fixed; { percentage of line layout error to be }
{ distributed among space characters. }
spcChar : Integer; { code of character that is to absorb }
{ the "major" line layout error }
minor : Fixed; { percentage of intercharacter distrib. }
ulLength: Fixed; { underline length. }
END;
Further Reference:
• PostScript Language Reference Manual, Adobe Systems Inc.
• Inside Macintosh, Volumes II, V, and VI
• LaserWriter Reference Manual, Addison-Wesley
• Macintosh Technical Note M.IM.AppPictComments —
Every Picture [Comment] Tells Its Story, Don’t It?
• Macintosh Technical Note M.IM.PictAndPrinting —
Pictures and the Printing Manager
• develop Issue 3, “Meet PrGeneral” by Pete “Luke” Alexander
Adobe is a trademark of Adobe Systems Incorporated.
PostScript is a registered trademark of Adobe Systems Incorporated.
QD 11 - Pictures and Clip Regions
QuickDraw
Revised by: March 1988
Written by: Jennifer Jernigan January 1986
This note describes a problem that affects creation of QuickDraw pictures.
When a GrafPort is created, the fields in the GrafPort are given default values; one of these is the clip region, which is set to the rectangle (–32767, –32767, 32767, 32767). If you create a picture, then call DrawPicture with a destination rectangle that is not the same size as the picFrame without ever changing the default clip region, nothing will be drawn.
When the picture frame is compared with the destination rectangle and the picture is scaled, the clip region is scaled too. In the process of scaling, the clip region you end up overflows and becomes empty, and your picture doesn’t get drawn. If you call ClipRect(thePort^.portRect) before you record the picture, the picture will be drawn correctly. The clipping on the destination port when playing back the picture is irrelevant: once a picture is incorrectly recorded, it is too late.
Further Reference:
• QuickDraw
QD 12 - Plotting Small Icons
QuickDraw
Revised by: James Beninghaus October 1989
Written by: James Beninghaus & Dennis Hescox August 1989
This Technical Note discusses the 'SICN' resource format and how to plot one in a GrafPort.
Changes since August 1989: Corrected errors in the Pascal code and spruced up the rest.
Introduction
Apple first introduced the 'SICN' resource so that the Script Manager could represent which country specific resources are installed in the system by displaying a small icon in the upper right corner of the menu bar. You can pass a 'SICN' resource to the Notification Manager or Menu Manager, and they will draw it for you automatically—you should continue to let them do so. However, if you want to draw a small icon in your application’s window, then this Note can help.
What does a 'SICN' look like? Following is a 'SICN' representation of a dogcow to help answer this question:
SICN' FatBits
There is reason to believe that this representation is actually a baby dogcow. Due to the protective nature of parent dogcows, young dogcows are rarely seen. This one was spotted during a DTS meeting after it drew attention to itself by crying “moo! woof!”. (Note that this dogcow said “moo! woof!” because it was immature; adult dogcows naturally say, “Moof!”.)
'SICN' Resource
A 'SICN' resource contains any number of small icon bit images. Each small icon in a 'SICN' list describes a 16 by 16 pixel image and requires 32 bytes of storage. Like an 'ICN#' resource, there is no count of the number of icons stored in a 'SICN'. The following 'SICN' resource, in MPW Rez format, contains two small icons:
The Macintosh Toolbox interfaces do not describe all the necessary data structures needed to work with 'SICN' resources. As shown in the following example, defining the 'SICN' type as an array of 16 short integers and the handles and pointers to this array type make life much easier.
Pascal
TYPE
SICN = ARRAY[0 .. 15] of INTEGER;
SICNList = ARRAY[0 .. 0] of SICN;
SICNPtr = ^SICNList;
SICNHand = ^SICNPtr;
C
typedef short SICN[16];
typedef SICN *SICNList;
typedef SICNList *SICNHand;
The Missing Count
The 'SICN' resource does not provide a count to indicate the number of small icons contained within; however, you can easily determine this number by dividing the total size of the resource by the size of a single small icon.
Pascal
CONST
mySICN = 1984;
VAR
theSICN : SICNHand;
theSize : LONGINT;
theCount : LONGINT;
theIndex : LONGINT;
theSICN := SICNHand(GetResource('SICN', mySICN));
IF (theSICN <> NIL) THEN BEGIN
theSize := GetHandleSize(Handle(theSICN));
theCount := theSize DIV sizeof(SICN);
END;
C
#define mySICN 1984
SICNHand theSICN;
long theSize;
long theCount;
long theIndex;
theSICN = (SICNHand) GetResource('SICN', mySICN);
if (theSICN) {
theSize = GetHandleSize((Handle)theSICN);
theCount = theSize / sizeof(SICN);
}
The Plot 'SICN's
The example procedure PlotSICN draws one small icon of a 'SICN' resource. It takes the handle from theSICN and the position in the list from theIndex within the rectangle theRect of the current GrafPort.
Following is an example call to PlotSICN which plots all the small icons in a resource into the same rectangle:
Pascal
SetRect(theRect, 0, 0, 16, 16);
FOR theIndex := 0 TO theCount-1 DO
PlotSICN(theRect, theSICN, theIndex);
C
SetRect(&theRect, 0, 0, 16, 16);
for (theIndex = 0; theIndex < theCount ; ++theIndex)
PlotSICN(&theRect, theSICN, theIndex);
Because PlotSICN uses _CopyBits and _CopyBits can move memory, you should lock the handle to the 'SICN' once the resource is loaded. Notice that the PlotSICN procedure dereferences the 'SICN' handle, adds an offset, and copies the resulting value. If the 'SICN' list moves in memory at this time, the bitmap’s baseAddr is useless.
To play it safe, PlotSICN saves a copy of the master pointer flags associated with the relocatable block, locks the block with a call to _HLock, and restores the flags after calling _CopyBits. You should never examine, set, or clear these flags directly; you should always use the routines which are provided by the Memory Manager and Resource Manager. Note that it is not necessary to check the value of the flag after getting it.
/* restore the resource's locked/unlocked condition */
HSetState((Handle) theSICN, state);
}
}
That Was Easy
Now that you’ve seen it done, it looks pretty easy. With minor modifications, some of the techniques in this Note could also be used to plot a bitmap of any dimension.
Inspired by: Jim Friedlander, Rick Blair, and Rich Collyer
Using Color QuickDraw to draw off screen is a common requirement of applications and other kinds of programs that run on the Macintosh. This Note discusses what Color QuickDraw needs in a graphics environment and how to create one for off-screen drawing. A brief discussion of GWorlds, which are off-screen graphics environments that are set up by the system, is given to help you decide whether to use them or the do-it-yourself techniques described in this Note for setting up an off-screen graphics environment. The author’s intent is to provide concepts and routines for creating an off-screen graphics environment, and also to explain why existing routines for off-screen drawing act as they do.
Many, many thanks go to Guillermo Ortiz, Konstantin Othmer, Bruce Leak, and Jon Zap for all their expertise on this subject, Rich Collyer, Rick Blair, and Jim Friedlander for paving the way, and especially to all people who inspired this update by asking great off-screen drawing questions.
Changes since October 1991: A very embarrassing bug was found in CreateOffScreen and UpdateOffScreen. If you try to create a 16- or 32-bit off-screen graphics environment, you’ll just get a paramErr. It won’t do that now.
Off-Screening
The Macintosh, as with every other CPU ever made by Apple, has memory-mapped video. That is, what you see on the screen is just the visual representation of a part of memory that’s reserved for the video hardware (that’s stretching the truth just a bit in the case of the text screens of the original Apple computer, the Apple II line, and the Apple III because there’s also a character generator in those, but the overall process still looks roughly the same). If you change the contents of a memory location in this part of memory, then you’ll see the corresponding location on the screen change when the video hardware draws the next frame or field of video. The resident raster graphics package, QuickDraw in the case of the Macintosh, draws images by stuffing the right values into the right places in the part of memory reserved for the video display. The resulting image on the screen looks like a line or perhaps an oval if you asked QuickDraw to draw a line or an oval, or it could be an entire complex image if you asked QuickDraw to draw one. This is normal, on-screen drawing.
Because video memory is a part of RAM just like any other part of RAM in the memory map of the Macintosh (or almost like; video memory might exist on a NuBus™ video card, but it’s still RAM), QuickDraw can be told to draw into a part of memory that isn’t reserved for the video hardware, maybe into a part of your own application’s heap. When you tell QuickDraw to draw into a part of memory that’s not reserved for the video hardware, you can’t see any of the results. This is off-screen drawing. There are plenty of perfectly good reasons to do this, such as providing storage for a paint-style document or to smoothly animate an image, but the assumption here is that you have a perfectly good reason to do this so you’re more interested in the “how” of it instead of the “why” of it. If you need to know why, there are several books that cover off-screen drawing and the perfectly good reasons to do such a thing. A good place to start is Scott Knaster’s book, Macintosh Programming Secrets, referenced at the end of this Note.
This Note is divided into these major sections:
• The introduction is the part that you’re reading now.
• “The Building Blocks” provides an overview of the data structures that you need to tell Color QuickDraw to draw off screen.
• “Building the Blocks” discusses the construction and initialization of these data structures.
• “Playing With Blocks” shows an example of the use of these structures to draw off screen.
• “Put That Checkbook Away!” discusses some variations of these techniques to handle off-screen drawing for special cases.
• “The GWorld Factor” provides a brief overview of GWorlds, how to use them, and how they compare and contrast to the manual techniques that are described in most of this Note.
Those of you who aren’t quite sure whether to use GWorlds or the do-it-yourself techniques might want to skip ahead for a moment to “The GWorld Factor” just in case doing it yourself is a waste of time. In any case, it’s a good idea to read this whole Note because the concepts are mostly the same whether you’re using GWorlds or not. GWorlds just make the process a lot easier, and they let you take advantage of the 8•24 GC video card. But, we’re not in that section of the Note yet.
The Building Blocks
Before you can tell QuickDraw to draw off of the screen, you’ll need to build three major data structures: a CGrafPort, a PixMap, and a GDevice. You’ll also need a couple of tables that define the colors involved with drawing to and copying from the off-screen image: the color table and the inverse table. Of course, you’ll need the pixel image itself, which is often called the “pixel buffer” or the “image buffer” or the “off-screen buffer” or just “the buffer.” It’s always called the “pixel image” in this Note. It doesn’t necessarily buffer anything anyway.
The CGrafPort
A CGrafPort describes a drawing environment, and it’s the color version of the GrafPort structure that’s described on pages 147 through 155 in the QuickDraw chapter of Inside Macintosh Volume I. The drawing environment consists of, among other things, the size and location of the graphics pen, the foreground and background colors to use when something is drawn, the pattern to use, the region to clip all drawing to, and the portion of a pixel image that the CGrafPort logically exists in. Any initialized CGrafPort or GrafPort can be set as the current port through the _SetPort routine. The current port is a set of parameters that are implicitly passed to most QuickDraw routines.
The most important reason to build a new CGrafPort when you draw off screen rather than using an existing CGrafPort is so that switching between drawing to an off-screen graphics environment and drawing to one or more windows (each of which is an extended GrafPort or CGrafPort structure) on the screen is very easy. Some people use just one CGrafPort to share between on-screen and off-screen graphics environments, and switch their PixMap structures to switch between drawing on screen and drawing off screen. That does work, but if the off-screen and on-screen graphics environments have a different clipRgn, visRgn, pen characteristic, portRect, or any other characteristics that are different, then those must be switched at that time too. If you instead create a CGrafPort that’s dedicated to one graphics environment, then a simple call to _SetPort effectively switches all these things for you at once. That’s why every window on the screen comes with its own port. A simple call to _SetPort switches between the characteristics of each window even if each window has radically different drawing characteristics.
The CGrafPort data structure is more completely described in the “Color QuickDraw” chapter of Inside Macintosh Volume V, pages 49 through 52, and in the “Graphics Overview” chapter of Inside Macintosh Volume VI, pages 16-12 through 16-13.
The PixMap
A pixel image alone is just a formless blob of memory. Pixel maps, defined by the PixMap structure, describe pixel images, giving them a form and structure that’s suitable for Color QuickDraw to draw into them and copy from them. The PixMap structure tells you the dimensions and location in memory of the pixel image, its coordinate system, and the depth and format of the pixels. Pixel maps that describe indexed-color pixel images additionally describe the colors that are represented by the values of the pixels in the pixel image. This is done through the color table, also known as the color look-up table or CLUT. Color tables are attached to pixel maps through their pmTable field. Direct-color pixel images have pixel values that describe their own colors, and so color tables aren’t needed for those.
The PixMap structure is described in the “Color QuickDraw” chapter of Inside Macintosh Volume V, pages 52 through 55, and in the “Graphics Overview” chapter of Inside Macintosh Volume VI, pages 16-11 through 16-12. The concept of direct-color and indexed-color pixels is described in this same chapter on pages 16-16 through 16-18, and also in the “Color QuickDraw” chapter of the same volume on pages 17-4 through 17-10.
The GDevice
Graphics devices, defined by the GDevice structure, describe color environments. They’re the most misunderstood data structure when it comes to off-screen graphics environments for three major reasons: first, they’re not originally documented as being relevant to humans; second, they look as though they’re only for screens; and third, it looks as though color tables describe color environments. We can dispose of these myths here: graphics devices are documented as being useful to humanity in this Note at least; they’re critically important for both on-screen and off-screen drawing; and color tables describe the colors in pixel images, not color environments.
What’s all this about color environments? In theory, there are virtually three hundred trillion colors available with Color QuickDraw through the 48-bit RGBColor record. In reality, there are never this many colors available, and in fact there might be only two. Color QuickDraw maps the theoretical color that you specify to the pixel value of the closest available color in the current color environment. This can be done with a color table, but that’s not very efficient. Finding the closest available color to an RGBColor in a color table means searching the entire color table for that one closest color. If that’s done just once, then performance isn’t much of an issue, but if it’s done many times, the performance hit could be significant. A very bad case of this is _CopyBits, where every pixel value in the source image is converted to an RGBColor by looking it up in the color table of the source PixMap. If the color table of the destination PixMap had to be searched to find the closest available color for every pixel in the source PixMap, then the performance of even the most straightforward _CopyBits call could be a lot slower than it has to be.
To avoid this performance hit, the current GDevice provides an inverse table and a device type which are used to determine the available set of colors. Inverse tables are anticolor tables. Where color tables give you a color for a given pixel value, inverse tables give you a pixel value for a given color. Every conceivable color table has a corresponding conceivable inverse table, just as every positive real number has a corresponding negative real number, or every Mr. Spock has a corresponding Mr. Spock with a goatee. The device type specifies whether the color environment uses the indexed-color, fixed-color, or direct-color model. In the direct-color model, the inverse table is empty. Only the indexed-color and direct-color models are described in this Note.
When you specify a color in an indexed-color environment, Color QuickDraw takes the RGBColor specification and converts it into a value that can be used as an index into the inverse table of the current GDevice. To do this conversion, Color QuickDraw takes the top few significant bits of each color component and combines them into part of a 16-bit word, blue bits in the least significant bits, green bits right above it, and the red bits right above green bits. Any unused bits are in the most significant bits of the 16-bit word. The resulting 16-bit word is used as an index into the inverse table. The value in the inverse table at that index is the pixel value which best represents that color in the current color environment. The number of bits of each component that are used is determined by what’s called the “resolution” of the inverse table. Almost always, the resolution of an inverse table is four bits, meaning the most significant four bits of each component are used to form the index into the inverse table. Figure 1 shows how an RGBColor record is converted to an index into an inverse table when the inverse-table resolution is four.
Figure 1 Conversion of RGBColor Record to Inverse-Table Index
The same process is used when _CopyBits is called with an indexed-color destination. Each pixel in the source pixel image is converted to an RGBColor either by doing a table look-up of the source pixel map’s color table if the source pixel image uses indexed colors, or by expanding the pixel value to an RGBColor record if the source pixel image uses direct colors. The resulting RGBColor is then used to look up a pixel value in the inverse table of the current GDevice, and this pixel value is put into the destination pixel image.
If you specify a color in a direct-color environment, then the resulting RGBColor is converted to a direct pixel value by the processes that are shown on pages 17-6 through 17-9 of the “Color QuickDraw” chapter of Inside Macintosh Volume VI.
Usually, inverse-table look-up involves an extra step to find what are called “hidden colors” using proprietary information that’s stored at the end of the inverse table. With an inverse-table resolution of four, only 16 shades of any particular component can be distinguished, and that’s often not enough. An inverse table with a resolution of five is much larger, but it still only gives you 32 shades of any component. Hidden colors are looked up after the normal inverse-table look-up to give a much more accurate representation of the specified color in the current color environment than the inverse-table look-up alone can produce. Sometimes, most notably when the arithmetic transfer modes are used or if dithering is used, the hidden colors are ignored.
When a new color table is assigned to a PixMap or when its existing color table is modified, then a new corresponding inverse table should be generated for the GDevice that’ll be used when drawing into that environment. Normally, this happens automatically without you having to do any more than inform Color QuickDraw of the change. This is described in more detail in “Changing the Off-Screen Color Table” later in this Note.
Graphics devices are documented in the “Graphics Devices” chapter of Inside Macintosh Volume VI which supersedes the “Graphics Devices” chapter of Inside Macintosh Volume V. They’re also discussed in the “Graphics Overview” chapter of Inside Macintosh Volume VI, pages 16-13 through 16-14. The inverse-table mechanism is described in the “Color Manager” chapter of Inside Macintosh Volume V, pages 137 through 139.
All Together Now
There are a lot of different ways to put the three structures together, and this Note discusses the architecture that’s shown in Figure 2. This architecture is useful when you want a simple, atomic, off-screen graphics environment.
Figure 2 Relationships Between Structures for Off-Screen Drawing
Notice that there’s no way to get to the GDevice from the CGrafPort, nor is there a way to get to the CGrafPort from the GDevice, though the PixMap can be found through either one. Your application must keep track of both the CGrafPort and the GDevice.
Building the Blocks
As with just about any algorithm, there are many ways to put the different structures together that form an off-screen graphics environment. This section covers just one way to build the architecture that’s shown in Figure 2.
Building the CGrafPort
The CGrafPort structure is the easiest one to put together because the _OpenCPort routine initializes so many of the fields of the CGrafPort structure for you. It also allocates and initializes the structures that are attached to every CGrafPort, such as the visRgn, clipRgn, grafVars handle, and so forth. Most of these are initialized with values that are fine for general purposes, but the visRgn, clipRgn, and portRect fields should be set to the desired boundary rectangle of the off-screen graphics environment. What follows is an overview of each of the fields that you have to worry about when you’re setting up a CGrafPort for drawing off screen.
portPixMap handle to the off-screen PixMap. _OpenCPort initializes this field to a copy of the PixMap that’s attached to the gdPMap field of the current GDevice. An overview of setting up this PixMap for drawing off screen is given in “Building the PixMap” later in this Note.
portRect specifies the rectangular area of the associated pixel image that this CGrafPort controls. This field should be set to the desired rectangular area of the off-screen image because _OpenCPort doesn’t necessarily initialize it to this size. Usually, the top-left corner of this rectangle has the coordinates (0, 0), but not necessarily so.
visRgn handle to the region that specifies the visible area into which you can draw. _OpenCPort doesn’t necessarily initialize it to the size of the off-screen image, so it should be set to the same size and coordinates as the portRect and left at that. This field is more important for windows because parts of them can be hidden by other windows.
clipRgn handle to the region that specifies the logical area into which you can draw. _OpenCPort initializes it to cover the entire QuickDraw coordinate plane. It’s usually a good idea to set it to the same size and coordinates as the portRect to avoid problems if the clipRgn is scaled or translated, which causes its signed integer coordinates to overflow and turn it into an empty region. One of the most common cases of this occurs when a picture that’s created in this CGrafPort is drawn into a destination rectangle that’s any larger than or translated from the original picture frame. Everything in the picture, including the clip region, is scaled to fit the destination rectangle. If the clip region covers the entire QuickDraw coordinate plane, then its coordinates overflow their signed integer bounds, and the clip region becomes logically empty. The result is that nothing is drawn.
The CreateOffScreen routine in Listing 1 creates an off-screen graphics environment, given a boundary rectangle, pixel depth, and color table, and it returns a new off-screen CGrafPort and GDevice, along with an error code. The desired pixel depth in bits per pixel is given in the depth parameter. If the pixel depth is eight or less, then an indexed-color graphics environment is created and a color table is required in the colors parameter. If the pixel depth is 16 or 32 bits per pixel and 32-Bit QuickDraw is available, then a direct-color graphics environment is created and the colors parameter is ignored. If 32-Bit QuickDraw isn’t available, then a pixel depth of 16 or 32 bits per pixel results in CreateOffScreen doing nothing more than returning a parameter error. A description of CreateOffScreen is given following the listing.
MPW Pascal Listing 1
FUNCTION CreateOffScreen(
bounds: Rect; {Bounding rectangle of off-screen}
depth: Integer; {Desired number of bits per pixel in off-screen}
colors: CTabHandle; {Color table to assign to off-screen}
VAR retPort: CGrafPtr; {Returns a pointer to the new CGrafPort}
VAR retGDevice: GDHandle {Returns a handle to the new GDevice}
): OSErr;
CONST
kMaxRowBytes = $3FFE; {Maximum number of bytes in a row of pixels}
VAR
newPort: CGrafPtr; {Pointer to the new off-screen CGrafPort}
newPixMap: PixMapHandle; {Handle to the new off-screen PixMap}
newDevice: GDHandle; {Handle to the new off-screen GDevice}
qdVersion: LongInt; {Version of QuickDraw currently in use}
savedPort: GrafPtr; {Pointer to GrafPort used for save/restore}
savedState: SignedByte; {Saved state of color table handle}
bytesPerRow: Integer; {Number of bytes per row in the PixMap}
error: OSErr; {Returns error code}
BEGIN
(* Initialize a few things before we begin *)
newPort := NIL;
newPixMap := NIL;
newDevice := NIL;
error := noErr;
(* Save the color table’s current state and make sure it isn’t purgeable *)
IF colors <> NIL THEN
BEGIN
savedState := HGetState(Handle(colors));
HNoPurge(Handle(colors));
END;
(* Calculate the number of bytes per row in the off-screen PixMap *)
/* One Last Look Around The House Before We Go… */
if (error != noErr)
{
/* Some error occurred; dispose of everything we allocated */
if (newPixMap != nil)
{
DisposCTable( (**newPixMap).pmTable );
DisposPtr( (**newPixMap).baseAddr );
}
if (newDevice != nil)
{
DisposHandle( (Handle)(**newDevice).gdITable );
DisposHandle( (Handle)newDevice );
}
if (newPort != nil)
{
CloseCPort( newPort );
DisposPtr( (Ptr)newPort );
}
}
else
{
/* Everything’s OK; return refs to off-screen CGrafPort and GDevice */
*retPort = newPort;
*retGDevice = newDevice;
}
return error;
}
CreateOffScreen begins by making sure that the color table, if there is one, doesn’t get purged during the time that the off-screen graphics environment is created. Then, a sanity check is done for the given depth, bounds, and color table. The depth must be either 1, 2, 4, or 8 bits per pixel, or additionally 16 or 32 bits per pixel if 32-Bit QuickDraw is available. If these conditions aren’t satisfied, then it’s decided that there’s an error in the parameter list, and CreateOffScreen does nothing more. To determine whether 32-Bit QuickDraw is available or not, the _Gestalt routine is used. If _Gestalt returns a value that’s equal to or greater than the constant gestalt32BitQD, then 32-Bit QuickDraw is available and depths of 16 and 32 bits per pixel are supported. It’s not necessary to determine whether _Gestalt is available or not because it’s implemented as glue code in the Macintosh Programmer’s Workshop.
A check is then done to determine whether the number of bytes in each row of the off-screen pixel image is too much for QuickDraw to handle. Color QuickDraw can handle up to and including 16,382 ($3FFE) bytes in each row of any pixel image. If the required number of bytes per row exceeds this amount, then CreateOffScreen decides that there’s an error in the parameter list and does nothing more. The minimum number of bytes in a row that’s enough to cover the given boundary rectangle at the given pixel depth is calculated with the formula:
This formula multiplies the number of pixels across the PixMap by the pixel depth to get the number of bits, and then this is divided by eight to get the number of bytes. This division by eight looks very strange because the number of bytes per row must be even, so this formula takes advantage of integer division and multiplication to make the result come out even. This particular formula additionally makes sure that the number of bytes per row is a multiple of four. This helps optimize the performance of Color QuickDraw operations because it allows Color QuickDraw to refer to each row beginning on a long word boundary in memory.
The last sanity check is to make sure that a color table is given as a parameter if it’s needed. Indexed-color graphics environments need color tables, so if the given pixel depth is eight or less (which implies an indexed-color graphics environment) and the given color table is NIL, then CreateOffScreen decides that there’s an error in the parameter list and does nothing more. If the given pixel depth is 16 or 32 (which implies a direct-color graphics environment), then CreateOffScreen ignores the given color table.
If all the sanity checks succeed, then the off-screen CGrafPort is allocated using a call to _NewPtr, and then it’s initialized and opened as a CGrafPort by passing the resulting pointer to _OpenCPort. Because _OpenCPort makes the new CGrafPort the current port, the current port is first saved so that it can be restored as the current port when CreateOffScreen is done.
As mentioned above, the _OpenCPort doesn’t necessarily initialize the portRect, visRgn, and clipRgn of the new CGrafPort to the areas that are needed for any particular off-screen graphics environment. So, the given boundary rectangle is assigned to the portRect field, _RectRgn is called to make the visRgn equal to the given boundary rectangle, and _ClipRect is called to set the clipRgn so that it’s equal to the given boundary rectangle.
The PixMap in the portPixMap field needs to be initialized for off-screen drawing, and that’s handled by the SetUpPixMap routine that’s described and defined in “Building the PixMap” later in this Note. Similarly, the off-screen GDevice must be created and initialized. That’s handled by the CreateGDevice routine that’s described and defined in “Building the GDevice” later in this Note.
Once these things are done, CreateOffScreen returns a pointer to the off-screen CGrafPort in the retPort parameter and a handle to the off-screen GDevice in the retGDevice parameter. The way to use these references is described in “Playing With Blocks” later in this Note.
Building the PixMap
_OpenCPort initializes the portPixMap field of the CGrafPort it’s initializing with a copy of the PixMap of the current GDevice. When the CreateOffScreen routine described earlier executes, the current GDevice is unknown. So, all the fields of the PixMap that the new CGrafPort receives must be initialized so that it can be used for drawing off screen.* What follows is an overview of each of the PixMap fields and how they should be initialized for off-screen drawing.
baseAddr pointer to the off-screen pixel image. The off-screen pixel image is allocated as a nonrelocatable block in the heap. The size of this block of memory is calculated from the rowBytes field, described next, multiplied by the number of rows in the given boundary rectangle.
rowBytes number of bytes in each row of the pixel image. This value is calculated from the formula that’s given in the CreateOffScreen routine. The most significant bit of this field should be set so that Color QuickDraw knows that this is a PixMap rather than a BitMap. The maximum value, ignoring the most significant bit, is 16,382.
bounds defines the coordinate system and the dimensions of the pixel image. For most off-screen drawing, this should be a rectangle that covers the entire off-screen graphics environment.
pmVersion set of internally and externally defined flags. As of 32-Bit QuickDraw 1.2, only the baseAddr32 flag is defined externally. This flag is described in “Choosing Your Off-Screen Memory” later in this Note. For most off-screen drawing, this field is set to zero.
packType image compression scheme for pictures. The options for this field are discussed in the “Graphics Overview” chapter of Inside Macintosh Volume VI, pages 17-22 through 17-23. In this Note, image compression isn’t discussed so this field is set to zero.
packSize internally used field. This field is always set to zero.
hRes horizontal resolution of the pixel map. By default, the QuickDraw resolution is 72 dots per inch,which is the value this Note uses. This is a fixed-point field, so the actual value in this field is $00480000.
vRes vertical resolution of the pixel map. See the hRes description.
pixelType format of the pixels. In indexed-color pixel maps, this field holds zero. In direct-color pixel maps, this field holds the RGBDirect constant, which is equal to 16.
pixelSize number of bits in every pixel. For indexed-color pixels, this is 1, 2, 4, or 8 bits per pixel. For direct-color pixels, this is 16 or 32 bits per pixel.
cmpCount number of components in every pixel. In indexed-color pixel maps, this field is set to 1. In direct-color pixel maps, this field is set to 3. Sometimes it’s handy to set this field to 4 in 32-bit deep pixel maps when they’re being saved in a picture. See the “Color QuickDraw” chapter of Inside Macintosh Volume VI, page 17-23, for details about this.
cmpSize number of bits in each color component. In indexed-color pixel maps, this field is set to the same value that’s in the pixelSize field. In 16-bit deep direct pixel maps, this field is set to 5. In 32-bit deep direct pixel maps, this field is set to 8.
planeBytes not currently defined. This field is set to zero.
pmTable handle to the color table for indexed-color pixel maps. A method to create a color table is given in “About That Creation Thing . . .” later in this Note. In direct-color pixel maps, this field contains a handle to a dummy color table, and building one of these is shown in the SetUpPixMap routine in Listing 2.
pmReserved not currently defined. This field is set to zero.
(*This part of these routines really bothers me because it feels impure to initialize all the PixMap fields when _OpenCPort has initialized them already, just not in a way that’s any good for off-screen drawing. I tried creating the GDevice and PixMap first and then calling _OpenCPort so that it initializes its PixMap for off-screen drawing, but then you end up with two pixel maps and that makes this tougher to explain, or you have to dispose of one PixMap which seems worse than the method I’m using.)
The SetUpPixMap routine in Listing 2 initializes the PixMap that’s passed to it in the aPixMap parameter so that it can be used in an off-screen graphics environment. The depth, bounds, and color parameters are the same as the ones passed to the CreateOffScreen routine. The bytesPerRow parameter is the number of bytes in each row of the off-screen pixel image. A description of SetUpPixMap follows the listing.
MPW Pascal Listing 2
FUNCTION SetUpPixMap(
depth: Integer; {Desired number of bits/pixel in off-screen}
bound: Rect; {Bounding rectangle of off-screen}
colors: CTabHandle; {Color table to assign to off-screen}
bytesPerRow: Integer; {Number of bytes in each row of pixels}
aPixMap: PixMapHandle {Handle to the PixMap being initialized}
): OSErr;
CONST
kDefaultRes = $00480000; {Default resolution is 72 DPI; Fixed type}
VAR
newColors: CTabHandle; {Color table used for the off-screen PixMap}
offBaseAddr: Ptr; {Pointer to the off-screen pixel image}
error: OSErr; {Returns error code}
BEGIN
error := noErr;
newColors := NIL;
offBaseAddr := NIL;
(* Clone the clut if indexed color; allocate a dummy clut if direct color *)
(**aPixMap).pmTable = newColors; /* Handle to CLUT */
}
else
{
/* PixMap is direct */
(**aPixMap).pixelType = RGBDirect; /* Indicates direct */
(**aPixMap).cmpCount = 3; /* Have 3 components */
if (depth == 16)
(**aPixMap).cmpSize = 5; /* 5 bits/component */
else
(**aPixMap).cmpSize = 8; /* 8 bits/component */
(**newColors).ctSeed = 3 * (**aPixMap).cmpSize;
(**newColors).ctFlags = 0;
(**newColors).ctSize = 0;
(**aPixMap).pmTable = newColors;
}
}
else
error = MemError();
}
else
newColors = nil;
/* If no errors occurred, return a handle to the new off-screen PixMap */
if (error != noErr)
{
if (newColors != nil)
DisposCTable( newColors );
}
/* Return the error code */
return error;
}
SetUpPixMap begins by copying the given color table if an indexed-color graphics environment is being built, or allocating a dummy color table if a direct-color graphics environment is being built. A copy of the color table is made because this allows the given color table and the off-screen graphics environment’s color table to be manipulated independently without interfering with each other, and this lets the off-screen graphics environment routines manipulate the color table without needing to worry about whether the color table is a 'clut' resource or not. The dummy color table is made so that routines which assume that every PixMap has a color table won’t do something catastrophic if they find a NIL color table. The off-screen pixel image is then allocated as a nonrelocatable block in the application’s heap.
Some of the fields of a PixMap have to be initialized differently depending upon whether the indexed-color model or the direct-color model is being used. So, the fields that are the same regardless of the color model that’s being used are assigned first. Then the desired pixel depth is compared to 8. If the depth is less than or equal to 8, then the rest of the fields are initialized for the indexed-color model. Otherwise, the rest of the fields are initialized for the direct color model. In the case of the direct-color model, the dummy color table is initialized to have no CSpecArray entries and its ctSeed field is set to three times the component size. This dummy color table is then installed into the PixMap.
Once SetUpPixMap completes, the PixMap of the new CGrafPort is ready to hold an off-screen image. It’s not quite ready to be drawn into with Color QuickDraw though. To do that, the off-screen GDevice is still needed; the construction and initialization of the GDevice are covered in the next section.
Building the GDevice
The _OpenCPort routine automatically allocates and initializes a PixMap, and the SetUpPixMap routine reinitializes that existing PixMap. _OpenCPort doesn’t allocate nor initialize a GDevice, so one has to be created from scratch. Pages 21-20 through 21-21 of “The Graphics Devices Manager” chapter of Inside Macintosh Volume VI describe the _NewGDevice routine. This routine seems as though it’s the ticket to getting a GDevice for off-screen drawing, but it always allocates the new GDevice in the system heap. That’s not so good because if your program unexpectedly quits or if you just forget to dispose of the GDevice before you quit for real, the GDevice gets orphaned in the system heap. To prevent this from happening, _NewGDevice should be ignored and the off-screen GDevice should instead be allocated and initialized from scratch. What follows is a description of how each field of the GDevice structure should be initialized.
gdRefNum reference number of video driver. Off-screen graphics environments don’t need to have video drivers because there’s no video device associated with them, so this field is set to zero.
gdID used to identify specific GDevice structures from color-search procedures. This isn’t necessary for off-screen drawing, so this is normally set to zero.
gdType type of GDevice. This field is set to the constant clutType (equal to zero) for an indexed-color environment and set to the constant directType (equal to 2) for a direct-color environment.
gdITable handle to the inverse table. Initially, this field is set to an arbitrarily small handle. Later, the _MakeITable routine is used to resize and initialize this handle to a real inverse table.
gdResPref inverse-table resolution. When _MakeITable is called by QuickDraw, the value of this field is used as the inverse-table resolution. Almost all inverse tables have a resolution of 4. There are some cases when a inverse-table resolution of 5 is useful, particularly when the arithmetic transfer modes are used with _CopyBits. See “The GDevice” earlier in this Note.
gdSearchProc pointer to the color-search procedure. If a color-search procedure is needed, this field can be set later by calling the _AddSearch routine (see the “Color Manager” chapter of Inside Macintosh Volume V, pages 145 through 147). Usually, this field is just set to NIL and left at that.
gdCompProc pointer to the color-complement procedure. If a color-complement procedure is needed, this field can be set later by calling the _AddComp routine (see the “Color Manager” chapter of Inside Macintosh Volume V, pages 145 through 147). Usually, this field is set to NIL and left at that.
gdFlags flags indicating certain states of the GDevice. This field should initially be set to zeroes. After the GDevice has been built, these flags can be set with the _SetDeviceAttrs routine (see the “Graphics Devices Manager” chapter of Inside Macintosh Volume VI, pages 21-10 and 21-22).
gdPMap handle to a PixMap. A handle to the PixMap of the CGrafPort that was created earlier is put into this field.
gdRefCon miscellaneous data. _CalcCMask and _SeedCFill use this field as described on pages 71 through 72 of Inside Macintosh Volume V. Initially, this field is set to zero.
gdNextGD handle to next GDevice in the GDevice list. The system maintains a linked list of GDevice records in which there’s one GDevice for every screen, and the links are kept in this field. Off-screen GDevice structures should never be put into this list, so this field should be set to NIL.
gdRect rectangle of GDevice. Strictly speaking, this field is used only for screens, but it should be the same as the bounds rectangle of the off-screen PixMap.
gdMode current video mode. This field is used by video drivers to keep track of the current mode that the video device is in. For off-screen GDevice structures, this field should be set to -1.
gdCC… These four fields are used only with GDevice structures for screens. For off-screen GDevice structures, these fields should be set to zero.
gdReserved not currently defined. This field is set to zero.
The CreateGDevice routine shown below in Listing 3 allocates and initializes a GDevice structure. It takes the initialized off-screen PixMap in the basePixMap parameter and returns the initialized GDevice in the retGDevice parameter. If any error occurs, any memory that’s allocated is disposed of and the result code is returned as a function result.
MPW Pascal Listing 3
FUNCTION CreateGDevice(
basePixMap: PixMapHandle; {Handle to the PixMap to base GDevice on}
VAR retGDevice: GDHandle {Returns a handle to the new GDevice}
): OSErr;
CONST
kITabRes = 4; {Inverse-table resolution}
VAR
newDevice: GDHandle; {Handle to the new GDevice}
embryoITab: ITabHandle; {Handle to the embryonic inverse table}
CreateGDevice begins by allocating the GDevice structure and an embryonic form of the inverse table in the current heap. The inverse table is allocated as two zero bytes for now; it’ll be resized and initialized to be a real inverse table later in this routine. Then, each of the GDevice fields are initialized as described earlier.
After all the fields have been initialized, the gdFlags field is set through _SetDeviceAttribute. If the desired pixel depth is greater than 1, then the gdDevType bit is set. This indicates that the GDevice is for a color graphics environment. This bit should be set even if a gray-scale color table is used for this off-screen graphics environment. The noDriver bit is set because this is an off-screen GDevice and so there’s no associated video device driver.
Finally, the inverse table is resized and initialized by calling the _MakeITable routine. A handle to the two-byte embryonic inverse table that was created earlier in CreateGDevice is passed as a parameter, as is a handle to the off-screen color table and the preferred inverse-table resolution.
All Fall Down
Now that we have a way to create an off-screen graphics environment, there has to be a way to get rid of it too. The DisposeOffScreen routine shown in Listing 4 does this. The CreateOffScreen routine returns an off-screen graphics environment that’s represented by a CGrafPort and GDevice. The DisposeOffScreen routine takes the off-screen CGrafPort and GDevice and deallocates all the memory that’s associated with them including the CGrafPort and its dependent structures, the GDevice, the PixMap, the color table, and the inverse table.
MPW Pascal Listing 4
PROCEDURE DisposeOffScreen(
doomedPort: CGrafPtr; {Pointer to the CGrafPort we’re getting rid of}
doomedGDevice: GDHandle {Handle to the GDevice we’re getting rid of}
);
VAR
currPort: CGrafPtr; {Pointer to the current port}
currGDevice: GDHandle; {Handle to the current GDevice}
BEGIN
(* Check to see whether the doomed CGrafPort is the current port *)
GetPort(GrafPtr(currPort));
IF currPort = doomedPort THEN
BEGIN
(* It is; set current port to Window Manager CGrafPort *)
GetCWMgrPort(currPort);
SetPort(GrafPtr(currPort));
END;
(* Check to see whether the doomed GDevice is the current GDevice *)
currGDevice := GetGDevice;
IF currGDevice = doomedGDevice THEN
(* It is; set current GDevice to the main screen’s GDevice *)
SetGDevice(GetMainDevice);
(* Throw everything away *)
doomedGDevice^^.gdPMap := NIL;
DisposGDevice(doomedGDevice);
DisposPtr(doomedPort^.portPixMap^^.baseAddr);
IF doomedPort^.portPixMap^^.pmTable <> NIL THEN
DisposCTable(doomedPort^.portPixMap^^.pmTable);
CloseCPort(doomedPort);
DisposPtr(Ptr(doomedPort));
END;
MPW C Listing 4
void DisposeOffScreen(
CGrafPtr doomedPort, /* Pointer to the CGrafPort to be disposed of */
GDHandle doomedGDevice) /* Handle to the GDevice to be disposed of */
{
CGrafPtr currPort; /* Pointer to the current port */
GDHandle currGDevice; /* Handle to the current GDevice */
/* Check to see whether the doomed CGrafPort is the current port */
GetPort( (GrafPtr *)&currPort );
if (currPort == doomedPort)
{
/* It is; set current port to Window Manager CGrafPort */
GetCWMgrPort( &currPort );
SetPort( (GrafPtr)currPort );
}
/* Check to see whether the doomed GDevice is the current GDevice */
currGDevice = GetGDevice();
if (currGDevice == doomedGDevice)
/* It is; set current GDevice to the main screen’s GDevice */
One mildly tricky aspect of this is that we shouldn’t dispose of the current graphics environment. To prevent this, the current port is retrieved by a call to _GetPort. If it returns a pointer to the same port that DisposeOffScreen is disposing, then the current port is set to the Window Manager’s CGrafPort. That was an arbitrary choice, but it’s the most neutral. Similarly, the current GDevice is retrieved by a call to _GetGDevice. If it returns a handle to the same GDevice that DisposeOffScreen is disposing, then the current port is set to the main screen’s GDevice. Again, that’s an arbitrary, neutral choice.
The inverse table, GDevice, pixel image, and color table are disposed of. Before disposing of the color table, a check is first made to see whether it’s NIL. That’s because it’s reasonable, though not normal, for the PixMap not to have even a dummy color table if the direct-color model is being used. Then the CGrafPort is closed which deallocates all the pieces associated with the CGrafPort, including the PixMap. Once this is done, all the structures that were created by calling CreateOffScreen are deallocated.
Playing With Blocks
Now that these four routines with two entry points can create and dispose of off-screen graphics environments, how are they used? There are several phases to using an off-screen graphics environment: creating it, drawing into it, switching between it and other off-screen and on-screen graphics environments, copying images to and from it, and disposing of it. Listing 5 shows a routine called ExerciseOffScreen which is a very basic example of all of these phases.
MPW Pascal Listing 5
PROCEDURE ExerciseOffScreen;
CONST
kOffDepth = 8; {Number of bits per pixel in off-screen environment}
rGrayClut = 1600; {Resource ID of gray-scale clut}
rColorClut = 1601; {Resource ID of full-color clut}
VAR
grayPort: CGrafPtr; {Graphics environment for gray off screen}
grayDevice: GDHandle; {Color environment for gray off screen}
colorPort: CGrafPtr; {Graphics environment for color off screen}
colorDevice: GDHandle; {Color environment for color off screen}
savedPort: GrafPtr; {Pointer to the saved graphics environment}
savedDevice: GDHandle; {Handle to the saved color environment}
offColors: CTabHandle; {Colors for off-screen environments}
offRect: Rect; {Rectangle of off-screen environments}
circleRect: Rect; {Rectangles for circle-drawing}
count: Integer; {Generic counter}
aColor: RGBColor; {Color used for drawing off screen}
error: OSErr; {Error return from off-screen creation}
BEGIN
(* Set up the rectangle for the off-screen graphics environments *)
SetRect(offRect, 0, 0, 256, 256);
(* Get the color table for the gray off-screen graphics environment *)
offColors := GetCTable(rGrayClut);
(* Create the gray off-screen graphics environment *)
/* Dispose of the off-screen graphics environments */
DisposeOffScreen( grayPort, grayDevice );
DisposeOffScreen( colorPort, colorDevice );
}
}
}
Two off-screen graphics environments are created in the same way. A rectangle that’s 256 pixels wide by 256 pixels high and with its top-left coordinate at (0, 0) is created in the offRect local variable. 'clut' resources are loaded from the application’s resource fork to use as the color tables of the two off-screen graphics environments; a gray-scale 'clut' in the first case and a full-color 'clut' in the second case. Then, CreateOffScreen is called with the rectangle, color table, and a hard-coded pixel depth of eight bits per pixel.
If CreateOffScreen returns noErr in both cases, then the current graphics environment is saved so that it can be restored later. Graphics environments consist of the current port and the current GDevice. The current GrafPort or CGrafPort is saved with _GetPort. The current GDevice is saved with _GetGDevice.
The gray-scale off-screen graphics environment is set as the current graphics environment by calling _SetPort with its CGrafPort and calling _SetGDevice with its GDevice. A vertical gray ramp is drawn into this graphics environment with the usual set of QuickDraw calls. This graphics environment’s pixel image is then copied to the full-color off-screen graphics environment with dithering and colorization with green (dithering requires 32-Bit QuickDraw and consistent colorization requires system software version 7.0; both of these features are described in Konstantin Othmer’s article “QuickDraw’s CopyBits Procedure: Better Than Ever in System 7.0” in Issue 6 of develop). Before this copy happens, the full-color off-screen graphics environment must be set as the current one. Once this is done, _CopyBits can properly map colors from the gray-scale off-screen graphics environment to the full-color one which gets a green ramp image.
Red, green, and blue concentric circles are drawn into the full-color off-screen graphics environment over the green ramp. This image is then copied to the graphics environment that was the current one when ExerciseOffScreen was called. To do this, the saved graphics environment is set as the current one by what should now be the familiar calls to _SetPort and _SetGDevice. The off-screen image is then copied to the saved graphics environment with _CopyBits.
Finally, the two off-screen graphics environments are disposed of by calling the DisposeOffScreen routine that’s defined in the section “All Fall Down” earlier in this Note.
Put That Checkbook Away!
The previous section covered the basics of creating and using off-screen graphics environments. This is good enough for many, if not most, needs of off-screen drawing. But there are variations to creating and maintaining an off-screen graphics environment for specific cases. This section discusses a few of the more common cases.
About That Creation Thing . . .
The CreateOffScreen routine, defined in Listing 1, takes three pieces of information: the boundary rectangle, the desired pixel depth, and the desired color table. But there’s much more to these pieces than ExerciseOffScreen shows. This section describes these pieces in more detail.
The first parameter to CreateOffScreen is a rectangle which determines the size and coordinate system of the off-screen graphics environment. Usually, the top-left corner of the rectangle has the coordinate (0, 0) because it’s usually easiest to draw everything using coordinates that can also be thought of as the horizontal and vertical distance in pixels from the top-left corner of the graphics environment. But in some cases, it’s more convenient to have the (0, 0) coordinate somewhere else, and passing CreateOffScreen a rectangle with a nonzero coordinate in the top-left corner is an easy way to do this. The coordinate system can be translated after the off-screen graphics environment is created by using the _SetOrigin routine that’s described on pages 153 through 155 of Inside Macintosh Volume I.
Warning: As Inside Macintosh Volume I, page 154, notes, the clip region of the port “sticks” to the coordinate system when you call _SetOrigin. If _SetOrigin offsets the coordinate system by a large amount, then the clip region might be moved completely outside of the port’s drawing area, and nothing can be drawn into that port. After calling _SetOrigin, you should set the clip region so that you can continue drawing into the port.
The number of bits per pixel implies the maximum number of available colors in a graphics environment, at least roughly speaking. The relationship between the number of bits per pixel and the number of available colors is discussed in the “Graphics Overview” chapter of Inside Macintosh Volume VI, pages 16-8 through 16-9.
If an indexed-color graphics environment is being made, then a color table must be passed to CreateOffScreen. In ExerciseOffScreen, the color table is retrieved from a 'clut' resource that’s in the application’s resource fork with a call to _GetCTable. Because CreateOffScreen clones this color table, this 'clut' resource can be purgeable so that it can be thrown out if its memory is needed for other purposes. _GetCTable can also be passed some special constants that tell it to allocate various system color tables that can also be passed to CreateOffScreen. These special constants are described on page 17-18 of the “Color QuickDraw” chapter of Inside Macintosh Volume VI. _GetCTable allocates memory for these system color tables, so they should be disposed of after you’re done with them.
A color table could also be built from scratch by allocating it with a call to _NewHandle and then initializing it by hand. The ColorTable structure is documented on pages 48 through 49 of Inside Macintosh Volume V. Here’s what each of the fields should be set to:
ctSeed identification value. This is an arbitrary value that should be changed any time the contents of the color table change so that the inverse table can be kept current. When Color QuickDraw draws anything, it compares the ctSeed of the color table of the PixMap of the current GDevice against the iTabSeed field of the inverse table of the current GDevice. If they’re the same, then Color QuickDraw uses colors according to that inverse table. If they’re different, then Color QuickDraw first rebuilds the inverse table according to the new color table’s contents and its iTabSeed is set to the value of the new color table’s ctSeed; then the rebuilt inverse table is used.
When _CopyBits is called with the srcCopy transfer mode, the ctSeed fields of the source and destination pixel maps are compared. If they’re the same, then _CopyBits simply transfers the source pixels to the destination with no mapping of colors. If they’re different, then _CopyBits checks each entry of the color tables to determine whether they have the same colors for the same pixel values. If they do, then _CopyBits again simply transfers the source pixels to the destination with no mapping of colors. If they don’t, then _CopyBits maps colors in the source PixMap to the colors in the current graphics environment according to the inverse table of the current GDevice. The ctSeed field of a color table should be changed whenever its contents are changed so that _CopyBits doesn’t make the wrong assumptions about the equality of the source and destination color tables.
You can get a seed value for a new color table by assigning to it the result of the _GetCTSeed routine, documented in the “Color Manager” chapter of Inside Macintosh Volume V, page 143. If the contents of an existing color table are changed, then it should be passed to the _CTabChanged routine which assigns a new value to its ctSeed field. If the _CTabChanged routine isn’t available (it’s available with 32-Bit QuickDraw and is included with the system beginning with system software version 7.0), then the ctSeed field should be given a new value with another call to _GetCTSeed.
ctFlags indicates the Boolean characteristics of a color table. If the most significant bit of ctFlags is clear, then the value field of each ColorSpec entry in the ctTable array is interpreted as the pixel value for the color that’s specified in the rgb field in the same ColorSpec entry. You can build a color table with nonconsecutive pixel values this way. If this bit is set, then all the value fields in the color table are ignored and the index of each ColorSpec record in the ctTable array is that record’s pixel value. It’s your choice whether to clear this bit and set the value fields or set this bit and ignore the value fields; traditionally this bit is clear for off-screen color tables.
If the next most significant bit of ctFlags is set, then the value field of each ColorSpec record in the ctTable array is used by _CopyBits as an index into the color palette that’s attached to the destination window, and the rgb field is ignored. This is documented in the “Palette Manager” chapter of Inside Macintosh Volume VI, page 20-17.
The other bits are reserved for future use. If you create a color table from scratch, these other bits must be set to zero. If you use a color table that’s generated by the system, then these bits must be preserved.
ctSize the number of color table entries minus 1. Normally, this field is set to 1, 3, 15, or 255 for 1-, 2-, 4-, and 8-bits per pixel, respectively. In special cases, it’s reasonable to have less than the maximum number of entries for the pixel depth. For example, a color table for an 8-bit per pixel graphics environment could have just 150 entries, in which case the ctSize field should hold 149. For this case, it’s still important to allocate as much space in the color table for the maximum number of entries for a pixel depth and clear the entries you’re not using to zero because some parts of Color QuickDraw assume the size of a color table based on the pixel depth.
ctTable array of colors and pixel values. This table defines all the available colors in the color table and their pixel values. The value field of each ColorSpec record indicates that color’s pixel value if the most significant bit of ctFlags is clear. It’s ignored if the most significant bit of ctFlags is set. The value field is used as an index into a palette if the next most significant bit of ctFlags is set, in which case the rgb field is ignored. See the discussion of the ctFlags field earlier in this Note for more details.
Warning: Color QuickDraw’s text-drawing routines assume that the color table of the destination graphics environment has the maximum number of colors for the pixel depth of the graphics environment, and that white is the first entry in the color table and black is the last entry. If these conditions aren’t satisfied, then the resulting image is unpredictable.
The code fragment in Listing 6 shows how to allocate a 256-entry color table from scratch. Color tables have a variable size, so the _NewHandle call has to calculate the size of the ColorTable record plus the maximum number of color table entries for the pixel depth multiplied by the size of a ColorSpec record. kNumColors - 1 is used in the calculation because the size of the ColorTable record includes the size of one ColorSpec entry in most development environments.
MPW Pascal Listing 6
CONST
kNumColors = 256; {Number of color table entries}
VAR
newColors: CTabHandle; {Handle to the new color table}
After you create an off-screen graphics environment with certain dimensions, you might later want to change its size, depth, or color table without creating a completely new graphics environment from scratch and without needing to redraw the existing image. The UpdateOffScreen routine in Listing 7 shows just one way to do this. It takes the same parameters that CreateOffScreen (defined in Listing 1) does, but instead of creating a new CGrafPort and GDevice, it alters the ones that you pass through the updPort and updGDevice parameters. If the newBounds parameter specifies an empty rectangle, then the existing boundary rectangle for the off-screen graphics environment is used. Similarly, if newDepth is zero, then the existing depth is used; and if the newColors parameter is NIL, then the existing color table is used. UpdateOffScreen alters the given CGrafPort and GDevice to the new settings, but it completely replaces the PixMap. After all the alterations are made, the old PixMap’s image is copied to the new PixMap’s image, and then the old PixMap and its image are disposed.
MPW Pascal Listing 7
FUNCTION UpdateOffScreen(
newBounds: Rect; {New bounding rectangle of off-screen}
newDepth: Integer; {New number of bits per pixel in off-screen}
newColors: CTabHandle; {New color table to assign to off-screen}
updPort: CGrafPtr; {Returns a pointer to the updated CGrafPort}
updGDevice: GDHandle {Returns a handle to the updated GDevice}
): OSErr;
CONST
kMaxRowBytes = $3FFE; {Maximum number of bytes per row of pixels}
VAR
newPixMap: PixMapHandle; {Handle to the new off-screen PixMap}
oldPixMap: PixMapHandle; {Handle to the old off-screen PixMap}
bounds: Rect; {Boundary rectangle of off-screen}
depth: Integer; {Depth of the off-screen PixMap}
bytesPerRow: Integer; {Number of bytes per row in the PixMap}
colors: CTabHandle; {Colors for the off-screen PixMap}
savedFore: RGBColor; {Saved foreground color}
savedBack: RGBColor; {Saved background color}
aColor: RGBColor; {Used to set foreground and background color}
qdVersion: LongInt; {Version of QuickDraw currently in use}
savedPort: GrafPtr; {Pointer to GrafPort used for save/restore}
savedDevice: GDHandle; {Handle to GDevice used for save/restore}
savedState: SignedByte; {Saved state of color table handle}
error: OSErr; {Returns error code}
BEGIN
(* Initialize a few things before we begin *)
newPixMap := NIL;
error := noErr;
(* Keep the old bounds rectangle, or get the new one *)
IF EmptyRect(newBounds) THEN
bounds := updPort^.portRect
ELSE
bounds := newBounds;
(* Keep the old depth, or get the old one *)
IF newDepth = 0 THEN
depth := updPort^.portPixMap^^.pixelSize
ELSE
depth := newDepth;
(* Get the old clut, or save new clut’s state and make it nonpurgeable *)
IF newColors = NIL THEN
colors := updPort^.portPixMap^^.pmTable
ELSE
BEGIN
savedState := HGetState(Handle(newColors));
HNoPurge(Handle(newColors));
colors := newColors;
END;
(* Calculate the number of bytes per row in the off-screen PixMap *)
/* Get rid of the old PixMap and its dependents */
DisposPtr( (**oldPixMap).baseAddr );
DisposeCTable( (**oldPixMap).pmTable ) ;
DisposHandle( (Handle)oldPixMap );
}
}
else
error = MemError();
}
/* Restore the given state of the color table */
if (colors != nil)
HSetState( (Handle)colors, savedState );
/* One Last Look Around The House Before We Go… */
if (error != noErr)
{
/* Some error occurred; dispose of everything we allocated */
if (newPixMap != nil)
{
if ((**newPixMap).pmTable)
DisposCTable( (**newPixMap).pmTable );
if ((**newPixMap).baseAddr)
DisposPtr ( (**newPixMap).baseAddr );
DisposHandle( (Handle)newPixMap );
}
}
return error;
}
UpdateOffScreen begins by checking the boundary rectangle, depth, or color table for emptiness, zero, or NIL, respectively. If any these satisfy that condition, then the existing characteristic is used. Next, the same sanity check that CreateOffScreen uses is done. If this sanity check succeeds, then a new PixMap is allocated, and then it’s initialized by the SetUpPixMap routine that’s given in Listing 2 which gives the new PixMap a new pixel image and its own copy of the color table. This new PixMap is installed into the CGrafPort after saving the reference to the old PixMap. Then, the portRect, visRgn, and clipRgn of the CGrafPort are set to the new boundary rectangle, as is the gdRect of the GDevice. The gdType of the GDevice is set either for the indexed-color or direct-color model, the gdPMap is set to the new PixMap, and the device attributes are set according to the pixel depth. Details about the settings for the CGrafPort and GDevice are in “Building the CGrafPort” and “Building the GDevice,” respectively, earlier in this Note.
At this point, the off-screen graphics environment is ready with its new characteristics, but it has garbage for an image because nothing has been drawn into it yet. The old PixMap, pixel image, and color table are still around, so _CopyBits transfers the old image into the altered graphics environment. _CopyBits handles the mapping from the old image’s characteristics to the new characteristics, so the altered graphics environment gets the best possible representation of the old image according to its new characteristics.
Changing the Off-Screen Color Table
Sometimes, it’s useful to change some or all of the colors in an off-screen color table, or to replace the off-screen color table with another one, so that the existing image in an indexed-color graphics environment appears with new colors. For example, if you had an off-screen image of a blue car and wanted to see what it looked like in green, you could change all of the shades of blue in the off-screen color table to green, and then _CopyBits the image to the screen. Notice that this is different from calling the UpdateOffScreen routine in the previous section with a different color table. That routine tries to reproduce the colors from the original image as best it can in the new set of colors. This section discusses the case in which you want the image’s colors to change.
The most obvious part of doing this is simply to get the color table from the off-screen pixel map’s pmTable field and modify the entries, or to dispose of the off-screen graphics environment’s current color table and assign the new one to it. There’s one more step to complete the process though. The discussion about GDevice records in “The Building Blocks” in this Note discusses inverse tables and how they go hand-in-hand with color tables. If you alter or replace the color table, you have to make sure that the inverse table of the off-screen drawing environment is rebuilt according to the new colors because Color QuickDraw uses that inverse table to know what pixel values to use for the specified color. You don’t have to rebuild the inverse table explicitly as long as you tell Color QuickDraw that the color table changed. To do this, all you have to do is make sure that the ctSeed of the changed or altered color table is set to a new value. And to do this, you can simply call _CTabChanged, which is documented on page 17-26 of the “Color QuickDraw” chapter of Inside Macintosh Volume VI. _CTabChanged is available beginning with 32-Bit QuickDraw and it’s available in system software version 7.0. If this routine isn’t available, then you can still tell Color QuickDraw that the color table has been changed by calling _GetCTSeed and assigning its result directly to your new color table’s ctSeed field.
The next time you draw into this off-screen drawing environment, Color QuickDraw checks the ctSeed of the environment’s color table against the iTabSeed of the inverse table of the environment’s GDevice. Because you changed the ctSeed of the color table either through _CTabChanged or _GetCTSeed, these two seeds are different so Color QuickDraw automatically rebuilds the inverse table of the current GDevice and then it copies the ctSeed of the color table to the iTabSeed of the rebuilt inverse table. Then drawing continues normally.
Follow That Screen!
One common need of off-screen graphics environments is that they have a depth and color table that matches a screen. The CreateOffScreen routine requires a color table for indexed-color environments, and a pixel depth. Because there can be more than one screen attached to a Macintosh system, you have to decide which screen’s depth and color table you should use. Typically, the depth and color table of the deepest screen that contains the area that you’re interested in (probably the area of a window) is used. Another option is to use the depth and color table of the screen that has the largest area of intersection with the area that you’re interested in. To find the depth and color table of the screen on which you want to base an off-screen graphics environment, you must use the list of graphics devices for all screens which is maintained by the system. Every GDevice record for a screen has a handle to that screen’s PixMap, and you can find the screen’s depth and color table there.
Listing 8 shows a routine called CreateScreenOffScreen which creates an off-screen graphics environment that has the depth and color table of a selected screen. The first parameter, bounds, specifies the rectangular part of the screen area in which you’re interested in global coordinates. The screenOption parameter specifies how you want the screen to be chosen. If you pass kDeepestScreen in this parameter, CreateScreenOffScreen creates the new off-screen graphics environment with the depth and color table of the deepest screen that intersects the bounds rectangle. If you instead pass kLargestScreenArea, then the new off-screen graphics environment is created with the depth and color table of the screen with the largest area of intersection with the bounds rectangle.
MPW Pascal Listing 8
TYPE
ScreenOpt = (kDeepestScreen, kLargestAreaScreen);
FUNCTION CreateScreenOffScreen(
bounds: Rect; {Global rectangle of part of screen to save}
screenOption: ScreenOpt; {Use deepest or largest intersection area screen?}
VAR retPort: CGrafPtr; {Returns a pointer to the new CGrafPort}
VAR retGDevice: GDHandle {Returns a handle to the new GDevice}
): OSErr;
VAR
baseGDevice: GDHandle; {GDevice to base off-screen on}
aGDevice: GDHandle; {Handle to each GDevice in the GDevice list}
basePixMap: PixMapHandle; {baseGDevice’s PixMap}
maxArea: LongInt; {Largest intersection area found}
area: LongInt; {Area of rectangle of intersection}
commonRect: Rect; {Rectangle of intersection}
normalBounds: Rect; {bounds rectangle normalized to (0, 0)}
error: Integer; {Error code}
BEGIN
error := noErr;
(* Different screen options require different algorithms *)
IF screenOption = kDeepestScreen THEN
(* Graphics Devices Manager tells us the deepest intersecting screen *)
baseGDevice := GetMaxDevice(bounds)
ELSE IF screenOption = kLargestAreaScreen THEN
BEGIN
(* Get a handle to the first GDevice in the GDevice list *)
aGDevice := GetDeviceList;
(* Keep looping until all GDevices have been checked *)
maxArea := 0;
baseGDevice := NIL;
WHILE aGDevice <> NIL DO
BEGIN
(* Check to see whether screen rectangle and bounds intersect *)
IF SectRect(aGDevice^^.gdRect, bounds, commonRect) THEN
BEGIN
(* Calculate area of intersection *)
area := LongInt(commonRect.bottom - commonRect.top) *
LongInt(commonRect.right - commonRect.left);
(* Keep track of largest area of intersection so far *)
IF area > maxArea THEN
BEGIN
maxArea := area;
baseGDevice := aGDevice;
END;
END;
(* Go to the next GDevice in the GDevice list *)
aGDevice := GetNextDevice(aGDevice);
END;
END
ELSE
error := paramErr;
(* If no screens intersect the bounds, baseDevice is NIL *)
Finding the deepest screen that intersects an on-screen area is trivially easy because there’s a Graphics Devices Manager routine that finds it called _GetMaxDevice which is documented on page 21-22 of the “Graphics Devices Manager” chapter of Inside Macintosh Volume VI. The rectangle in global coordinates of the screen area you’re interested in is passed to _GetMaxDevice, and it returns a handle to the deepest screen that intersects that area, even if the area of intersection is as small as one pixel. If no screens intersect that area, then _GetMaxDevice returns NIL.
Finding the GDevice of the screen that has the maximum area of intersection with the screen area you’re interested in isn’t quite so easy because there’s no single Graphics Devices Manager routine to find this GDevice; you have to search the GDevice list yourself. You can get a handle to the first GDevice in the list by calling _GetDeviceList, and you can get a handle to each successive GDevice by calling _GetNextDevice. _GetDeviceList is documented on pages 21-21 through 21-22 of the “Graphics Devices Manager” chapter of Inside Macintosh Volume VI, and _GetNextDevice is documented on page 21-22 of the same chapter. For each GDevice in the list, the area of intersection between the bounds and the gdRect of the GDevice is calculated. If the calculated area is the largest area of intersection found so far, then that area and the GDevice of that screen are remembered.
Once a winning GDevice has been chosen, either by being the deepest intersecting GDevice or the GDevice with the largest intersecting area, then CreateOffScreen routine is called with the pixel depth and color table of the PixMap of the GDevice, and the bounds rectangle normalized so that its top-left coordinate has the coordinates (0, 0). CreateOffScreen returns with the new off-screen graphics environment, and CreateScreenOffScreen returns this to the caller.
Choosing Your Off-Screen Memory
The CreateOffScreen routine in Listing 1 creates an off-screen graphics environment with its pixel image allocated as a nonrelocatable block in the application’s heap. But this isn’t the only way that the pixel image can be allocated. Pixel images can be big, and big blocks of nonrelocatable memory in your heap can be expensive in terms of performance, and they can cause a bad case of heap fragmentation. Why not put the pixel image in a relocatable block of memory instead? If there isn’t much free memory in your heap and if MultiFinder or system software version 7.0 is running, there’s memory that’s not being used by any open applications, called temporary memory (formerly called MultiFinder temporary memory). Why not use this area of memory for the pixel image? Some people have NuBus cards with plenty of memory on them. Why not move the pixel image out of the heaps altogether and instead use NuBus memory for the pixel image? All of these things can be done with simple modifications to what’s been discussed in this Note, and these modifications are discussed in the next few paragraphs.
How can pixel images be relocatable? After all, pixel images are referred to only by the baseAddr field of a PixMap, and the baseAddr is a pointer, not a handle. It’s true that while QuickDraw is being used to draw into a graphics environment, the pixel image had better not move or else QuickDraw will start drawing over the area of memory that the pixel image used to be rather than where it is. But if QuickDraw isn’t doing anything with the graphics environment, then it doesn’t care what happens to the pixel image as long as the baseAddr points to it once QuickDraw starts drawing into the graphics environment. This implies a strategy: allocate the pixel image as a relocatable block and let it float in the heap; when QuickDraw is about to to draw into the graphics environment or to copy from it, lock the pixel image and copy its master pointer into the baseAddr field of the PixMap; when the drawing or copying is finished, unlock the pixel image. There are many ways to implement this, and Listing 9 shows a code fragment for one very simple method.
MPW Pascal Listing 9
...
(* Allocate the pixel image; use long multiplication to avoid overflow *)
Listing 9 starts with a code fragment from the SetUpPixMap routine that’s modified so that it allocates a new handle for the off-screen pixel image instead of a new pointer. This handle is saved in the baseAddr field for now. When you’re about to draw into the off-screen graphics environment or to copy from it, the LockOffScreen routine in Listing 9 should be called with a pointer to the off-screen graphics environment’s CGrafPort as the parameter. It takes the handle to the pixel image from the baseAddr field of the off-screen graphics environment’s PixMap and passes it to _HLock which makes sure the pixel image can’t move in the heap. Then, the pixel image’s handle is dereferenced to get the master pointer to the pixel image, and this master pointer is copied into the baseAddr field. Now, QuickDraw can draw into or copy from the off-screen graphics environment.
When you’re finished drawing into the off-screen graphics environment, the pixel image should be unlocked, and the UnlockOffScreen routine in Listing 9 does this. The baseAddr field of the PixMap holds the pixel image’s master pointer, so this is passed to _RecoverHandle to get the pixel image’s handle. This handle is passed to _HUnlock to let the pixel image float in the heap again, and then this handle is saved in the baseAddr field.
One potentially useful addition to the LockOffScreen routine would be a call to _MoveHHi just before the call to _HLock. This helps reduce heap fragmentation while the pixel image is locked by moving it up as high in the heap as possible before locking it, allowing the other relocatable blocks to move without tripping over it. You have to be careful with _MoveHHi though because it not only moves the handle as high in the heap as possible, it moves other relocatable blocks out of the top of the heap to make room for the handle. This could involve moving huge amounts of memory, and it’s not unusual for _MoveHHi to take several seconds to do this.
How do you make an off-screen graphics environment that uses temporary memory for the pixel image? Temporary memory is allocated as handles, so there’s almost no difference between using temporary memory and using relocatable blocks in your own heap in the way that Listing 9 shows. All you have to do is replace the calls to _NewHandle, _HLock, and _HUnlock with calls to _TempNewHandle, _TempHLock, and _TempHUnlock. If temporary memory handles are real, then you don’t even have to replace the _HLock and _HUnlock calls—they work properly with temporary memory handles that are real.You can tell whether temporary memory handles are real or not by calling _Gestalt with the gestaltOSAttr selector. If the gestaltRealTempMemory bit is set, then all temporary memory handles are real. See the sections “About Temporary Memory” and “Using Temporary Memory” of Inside Macintosh Volume VI, pages 28-33 through 28-40.
How do you make an off-screen graphics environment that stores the pixel image on a NuBus memory card? The Macintosh Memory Manager doesn’t keep track of heaps on NuBus memory cards so it can’t be used to allocate memory on those cards, but if applications can use that card’s memory at will, then an application can set up the off-screen graphics environment with its pixel image in the NuBus card’s memory simply by setting the address of the card’s memory in the baseAddr field of the off-screen graphics environment’s PixMap instead of allocating anything.
If your NuBus memory card doesn’t require 32-bit addressing mode to access its memory, then setting the baseAddr to the address of the NuBus card’s memory is all you have to do. Some NuBus memory cards require its memory to be accessed in 32-bit addressing mode. Without 32-Bit QuickDraw, these memory cards can’t be used for storing the pixel image of an off-screen graphics environment because Color QuickDraw without 32-Bit QuickDraw always reads and writes pixel images in 24-bit addressing mode regardless of whether the pixel image is in main memory, on a NuBus video card, or on a NuBus memory card. With 32-Bit QuickDraw, Color QuickDraw automatically switches to 32-bit addressing mode before reading or writing a pixel image that’s on a video card. It won’t know to switch to 32-bit addressing mode if your off-screen graphics environment uses a pixel image on a NuBus memory card that’s not a video card, but you can tell it to make this switch by setting bit 2 of the pmVersion field of the PixMap for the off-screen graphics environment. This is normally done by logically ORing the pmVersion field with the predefined constant baseAddr32. See “About 32-Bit Addressing” in Issue 6 of develop, page 36, for more details about how QuickDraw handles addressing modes.
The GWorld Factor
In May 1989, 32-Bit QuickDraw was introduced as an extension to the system. While it had a lot of new features, the GWorld mechanism was the one that made the big news. GWorlds are off-screen graphics environments that you can have the system put together in one call. There’s no need for routines like CreateOffScreen, SetUpPixMap, or CreateGDevice—all of the off-screen graphics environment is set up with _NewGWorld. You can change most of its characteristics with _UpdateGWorld, set the current off-screen graphics environment with _SetGWorld, and get rid of the off-screen graphics environment with _DisposeGWorld. All the GWorld routines are described in the “Graphics Devices Manager” chapter of Inside Macintosh Volume VI. As an example, Listing 10 shows the same routine as the ExerciseOffScreen routine that’s shown in Listing 5, but Listing 10 uses GWorlds rather than the do-it-yourself routines that are defined in this Note.
MPW Pascal Listing 10
PROCEDURE ExerciseOffScreen;
CONST
kOffDepth = 8; {Number of bits per pixel in off-screen environment}
rGrayClut = 1600; {Resource ID of gray-scale clut}
rColorClut = 1601; {Resource ID of full-color clut}
VAR
grayPort: GWorldPtr; {Graphics environment for gray off screen}
colorPort: GWorldPtr; {Graphics environment for color off screen}
savedPort: GrafPtr; {Pointer to the saved graphics environment}
savedDevice: GDHandle; {Handle to the saved color environment}
offColors: CTabHandle; {Colors for off-screen environments}
offRect: Rect; {Rectangle of off-screen environments}
circleRect: Rect; {Rectangles for circle-drawing}
count: Integer; {Generic counter}
aColor: RGBColor; {Color used for drawing off-screen}
error: OSErr; {Error return from off-screen creation}
BEGIN
(* Set up the rectangle for the off-screen graphics environments *)
SetRect(offRect, 0, 0, 256, 256);
(* Get the color table for the gray off-screen graphics environment *)
offColors := GetCTable(rGrayClut);
(* Create the gray off-screen graphics environment *)
/* Copy the color off-screen environment to the current port */
SetGWorld( savedPort, savedDevice );
CopyBits( &((GrafPtr)colorPort)->portBits,
&((GrafPtr)savedPort)->portBits,
&colorPort->portRect,
&savedPort->portRect,
srcCopy, nil );
/* Dispose of the off-screen graphics environments */
DisposeGWorld( grayPort );
DisposeGWorld( colorPort );
}
}
}
_NewGWorld creates an off-screen graphics environment by creating a CGrafPort, PixMap, and GDevice—the same structures that you normally put together when you make an off-screen graphics environment yourself. In this aspect, and in fact in most aspects, there’s nothing magical about GWorlds. Do GWorlds make the CreateOffScreen, DisposeOffScreen, and their dependents useless? That depends on what your needs are. What follows are a few issues about off-screen drawing and how that determines whether you use your own routines, such as CreateOffScreen, to create and maintain off-screen graphics environments or whether you use GWorlds for the same purpose.
I Want the Best Performance!
As mentioned in the last paragraph, there’s nothing magical about GWorlds in most aspects. In one major aspect, there certainly is: the version of Color QuickDraw that runs with the 8•24 GC video card’s acceleration on knows about GWorlds and can cache their CGrafPort, PixMap, GDevice, inverse table, color table, and pixel image on the 8•24 GC card if there’s enough memory on it. When this is done, QuickDraw operations on the GWorld can be much faster than they’d normally be because the image data can stay in the card’s memory where the fast microprocessor is, and image data doesn’t have to move across NuBus in transfer operations between the GWorld and the screen. Additionally, these operations are executed asynchronously which increases the overall speed of your programs. For details about how the 8•24 GC card and GC QuickDraw work, see Guillermo Ortiz’s article, “Macintosh Display Card 8•24 GC: The Naked Truth,” in Issue 5 of develop.
8•24 GC QuickDraw doesn’t know about the off-screen graphics environments that you create, so it doesn’t cache its structures. All QuickDraw commands that move image data between the off-screen graphics environment and the screen have to move the data across NuBus, and that slows down the operation in comparison to keeping all the image data on the card.
If you want the highest possible drawing and copying performance with the 8•24 GC card, you must use GWorlds for your off-screen graphics environments.
I Want to Use a NuBus Memory Card for My GWorld’s Off-Screen Pixel Image
One common desire is to use a NuBus memory card to hold a pixel image. Because GWorlds are so easy to set up, and because GWorlds have all the same parts that you can make for an off-screen graphics environment, it’s tempting to make a GWorld and then point the baseAddr of the GWorld’s PixMap at the NuBus card’s memory. But GWorlds are designed to be fairly atomic structures, so they can’t be changed in this way. You can change a GWorld’s dimensions, depth, and color table because there’s a routine (_UpdateGWorld) that is designed to change these things, but you can’t change the pixel image without risking future compatibility.
If you want to have an off-screen graphics environment use a NuBus video card to store the pixel image, you should set up your own off-screen graphics environment rather than use GWorlds. This is covered earlier in this Note in “Choosing Your Off-Screen Memory.”
I Want My Program to Work on All System Software Releases
GWorlds have been around since 32-Bit QuickDraw was released (while system software version 6.0.3 was current). Until system software version 7.0, 32-Bit QuickDraw was an optional part of the system, so you aren’t guaranteed use of GWorlds even under recent system software releases. Obviously, if GWorlds aren’t available and your program still has to work with off-screen graphics environments, then there’s no choice but to use your own routines for creating, maintaining, and disposing of off-screen graphics environments. What’s usually done in these cases is to check via _Gestalt whether GWorlds are available or not. If they aren’t, then you create your off-screen graphics environment with your own routines. If they are, then you can use GWorlds without having to take up memory with your code for creating off-screen graphics environments yourself.
Are We There Yet?
Reliable, understandable, and maintainable off-screen drawing routines means not taking short-cuts. The most common problems that people run into with off-screen drawing routines is the appearance of strange colors and the gradual degradation of reliability as the program does more off-screen drawing. Building an off-screen graphics environment out of a CGrafPort, GDevice, and PixMap or by using GWorlds, combined with an understanding of how Color QuickDraw uses off-screen graphics environments, helps get rid of these problems. Hopefully, this Note helps you understand these things so that you can get better programs out the door faster.
Further Reference:
• Apple Computer, Inc., Inside Macintosh Volume I, Addison-Wesley, Reading, MA, 1985
• Apple Computer, Inc., Inside Macintosh Volume V, Addison-Wesley, Reading, MA, 1988.
• Apple Computer, Inc., Inside Macintosh Volume VI, Addison-Wesley, Reading, MA, 1991.
• Knaster, S., Macintosh Programming Secrets, Addison-Wesley, Reading, MA, 1988.
• Leak, B., “Realistic Color For Real-World Applications,” develop, January 1990, 4-21.
• Ortiz, G., “Braving Offscreen GWorlds,” develop, January 1990, 28-40.
• Ortiz, G., “Deaccelerated _CopyBits & 8•24 GC QuickDraw,” Macintosh Technical Note #289, January 1991.
• Ortiz, G., “Macintosh Display Card 8•24 GC: The Naked Truth,” develop, July 1990, 332-347.
• Othmer, K., “QuickDraw’s CopyBits Procedure: Better Than Ever in System 7.0,” develop, Spring 1991, 23-42.
• Tanaka, F., “Of Time and Space and _CopyBits,” Macintosh Technical Note #277, June 1990.
• Zap, J., F. Tanaka, J. Friedlander, and G. Jernigan, “Drawing Into an Off-Screen Bitmap,” Macintosh Technical Note #41, June 1990.
NuBus is a trademark of Texas Instruments.
QD 14 - QuickDraw’s Internal Picture Definition
QuickDraw
Revised by: Rick Blair November 1986
March 1988
Written by: Ginger Jenigan April 1985
This technical note describes the internal format of the QuickDraw picture data structure. This revision corrects some errors in the opcode descriptions and provides some examples.
This technical note describes the internal definition of the QuickDraw picture. The information given here only applies to QuickDraw picture format version 1.0 (which is always created by Macintoshes without Color QuickDraw). Picture format version 2.0 is documented in the Color QuickDraw chapter of Inside Macintosh. This information should not be used to write your own picture bottleneck procedures; if we add new objects to the picture definition, your program will not be able to operate on pictures created using standard QuickDraw. Your program will not know the size of the new objects and will, therefore, not be able to proceed past the new objects. (What this ultimately means is that you can’t process a new picture with an old bottleneck proc.)
Terms
An opcode is a number that DrawPicture uses to determine what object to draw or what mode to change for subsequent drawing. The following list gives the opcode, the name of the object (or mode), the associated data, and the total size of the opcode and data. To better interpret the sizes, please refer to page I-91 of the Using Assembly Language chapter of Inside Macintosh. For types not described there, here is a quick list:
opcode byte
mode word
point 4 bytes
0..255 byte
–128..127 signed byte
rect 8 bytes
poly 10+ bytes (starts with word size for poly (incl. size word)
region 10+ bytes (starts with word size for region (incl. size word)
fixed point long
pattern 8 bytes
rowbytes word (always even)
bit data rowbytes * (bounds.bottom - bounds.top) bytes
Each picture definition begins with a picsize (word), then a picframe (rect), and then the picture definition, which consists of a combination of the following opcodes:
Opcode Name Additional Data Total Size (bytes)
00 NOP none 1
01 clipRgn rgn 1+rgn
02 bkPat pattern 9
03 txFont font (word) 3
04 txFace face (byte) 2
05 txMode mode (word) 3
06 spExtra extra (fixed point) 5
07 pnSize pnSize (point) 5
08 pnMode mode (word) 3
09 pnPat pattern 9
0A thePat pattern 9
0B ovSize point 5
0C origin dh, dv (words) 5
0D txSize size (word) 3
0E fgColor color (long) 5
0F bkColor color (long) 5
10 txRatio numer (point), denom (point) 9
11 picVersion version (byte) 2
20 line pnLoc ( point ), newPt ( point ) 9
21 line from newPt ( point ) 5
22 short line pnLoc ( point ); dh, dv (-128..127) 7
23 short line from dh, dv (-128..127) 3
28 long text txLoc ( point ), count (0..255), text 6+text
29 DH text dh (0..255), count (0..255), text 3+text
2A DV text dv (0..255), count (0..255), text 3+text
2B DHDV text dh, dv (0..255), count (0..255), text 4+text
30 frameRect rect 9
31 paintRect rect 9
32 eraseRect rect 9
33 invertRect rect 9
34 fillRect rect 9
38 frameSameRect rect 1
39 paintSameRect rect 1
3A eraseSameRect rect 1
3B invertSameRect rect 1
3C fillSameRect rect 1
40 frameRRect rect ( ovalwidth, height; see 1, below ) 9
41 paintRRect rect ( ovalwidth, height; see 1, below ) 9
42 eraseRRect rect ( ovalwidth, height; see 1, below ) 9
43 invertRRect rect ( ovalwidth, height; see 1, below ) 9
44 fillRRect rect ( ovalwidth, height; see 1, below ) 9
48 frameSameRRect rect 1
49 paintSameRRect rect 1
Opcode Name Additional Data Total Size (bytes)
4A eraseSameRRect rect 1
4B invertSameRRect rect 1
4C fillSameRRect rect 1
50 frameOval rect 9
51 paintOval rect 9
52 eraseOval rect 9
53 invertOval rect 9
54 fillOval rect 9
58 frameSameOval rect 1
59 paintSameOval rect 1
5A eraseSameOval rect 1
5B invertSameOval rect 1
5C fillSameOval rect 1
60 frameArc rect, startAngle, arcAngle 13
61 paintArc rect, startAngle, arcAngle 13
62 eraseArc rect, startAngle, arcAngle 13
63 invertArc rect, startAngle, arcAngle 13
64 fillArc rect, startAngle, arcAngle 13
68 frameSameArc startAngle, arcAngle 5
69 paintSameArc startAngle, arcAngle 5
6A eraseSameArc startAngle, arcAngle 5
6B invertSameArc startAngle, arcAngle 5
6C fillSameArc startAngle, arcAngle 5
70 framePoly poly 1+poly
71 paintPoly poly 1+poly
72 erasePoly poly 1+poly
73 invertPoly poly 1+poly
74 fillPoly poly 1+poly
78 frameSamePoly (not yet implemented—same as 70, etc.) 1
79 paintSamePoly (not yet implemented) 1
7A eraseSamePoly (not yet implemented) 1
7B invertSamePoly (not yet implemented) 1
7C fillSamePoly (not yet implemented) 1
80 frameRgn rgn 1+rgn
81 paintRgn rgn 1+rgn
82 eraseRgn rgn 1+rgn
83 invertRgn rgn 1+rgn
84 fillRgn rgn 1+rgn
88 frameSameRgn (not yet implemented—same as 80, etc.) 1
maskRgn, packed bitData for each row packed bitData
A0 shortComment kind(word) 3
A1 longComment kind(word), size(word), data 5+data
FF EndOfPicture none 1
Notes
Rounded-corner rectangles use the setting of the ovSize point (see opcode $0B, above).
OpenPicture and DrawPicture set up a default set of port characteristics when they start. When drawing occurs, if the user’s settings don’t match the defaults, mode opcodes are generated. This is why there is usually a clipRgn code after the picVersion: the default clip region is an empty rectangle.
The only savings that the “same” opcodes achieve under the current implementation is for rectangles. DrawPicture keeps track of the last rectangle used and if a “same” opcode is encountered that requests a rectangle, the last rect. will be used (and no rectangle will appear in the opcode’s data).
This last section contains some Pascal program fragments that generate pictures. Each section starts out with the picture itself (yes, they’re dull) followed by the code to create and draw it, and concludes with a commented hex dump of the picture.
{variables used in all examples}
VAR
err: OSErr;
ph: PicHandle;
h: Handle;
r: Rect;
smallr: Rect;
orgr: Rect;
pstate: PenState; {are they in the Rose Bowl, or the state pen?}
90 0002 000A 0014 000F 001C {BitsRect rowbytes bounds (note that bounds is wider than smallr)}
000A 0014 000F 0019 {srcRect}
0000 0000 0014 001E {dstRect}
00 06 {mode=notSrcXor}
0000 0000 0000 0000 0000 {5 rows of empty bitmap (we copied from a
still-blank window)}
FF {fin}
Further Reference:
• QuickDraw
• Color QuickDraw
• Using Assembly Language
• Technical Note #59—Pictures and Clip Regions
QD 15 - RowBytes Revealed II
QuickDraw
Written by:Bill Guschwan May 1993
This Technical Note discusses the maximum rowBytes value for a pixMap.
Topics
• What is the largest value of rowBytes?
• Why does it have a limit?
• How do you increase the limit?
Introduction
This technical note concerns applications which create and read very large pixMaps. The rowBytes field defines the limits of the largest pixMap. For applications which use 32-Bit QuickDraw, this limit is well-defined: 0x3FFE. However, a few applications want to go beyond this limit, and under some extremely controlled situations, this limit can be extended to 0x7FFE. This note explains why the limit exists, and proposes a few difficult methods to exceed this limit. Along the way, proper parameter passing for CopyBits is discussed and a few debugging tips are thrown in.
Largest size of rowBytes of a pixMap
rowBytes is important because it defines the size of a pixMap. rowBytes had a largest size of 0x1FFE up through Color QuickDraw. The top three bits were reserved.
Version 1.0 of 32-Bit QuickDraw extended that limit to 0x3FFE: only the top two bits are reserved. QuickDraw uses the two bits for identification purposes. The top bit distinguishes between a pixMap and a bitMap. If the top bit of rowBytes is set, then QuickDraw knows the structure is a pixMap. The second highest bit is used by CopyBits, CopyMask, CopyDeepMask, SeedCFill, and CalcCMask to distinguish between their bitMap parameters. The implications of this identification scheme are fully discussed below.
3 Types of BitMap Parameters
CopyBits, CopyMask, CopyDeepMask, SeedCFill, and CalcCMask are routines which take bitMaps as their first two parameters. For historical and compatibility reasons, three types of parameters can be passed in: bitMap, pixMaps and the portBits field of a cGrafPort.
For the purposes of this note, I will focus on the CopyBits call, though my discussion will apply to the other four routines. The behavior of these five calls is identical for the bitMap parameters.
The BitMap
The bitMap parameter is actually a pointer to a bitMap. In Pascal, the pointer is implicit, since a bitMap structure is greater than 4 bytes in size and the Pascal compiler creates a pointer for any data structure greater than 4 bytes.
In C:
pascal void CopyBits(const BitMap *srcBits,const BitMap *dstBits,const Rect
*srcRect, const Rect *dstRect,short mode,RgnHandle maskRgn) = 0xA8EC;
With Color QuickDraw, CopyBits accepts pixMaps as BitMap parameters. This support, however, does not come without a cost. CopyBits needs a method to distinguish between the two parameters. As mentioned above, the top bit of rowBytes distinguishes between pixMaps and bitMaps. While this bit allows pixMaps to be passed in to CopyBits, the size of rowBytes is diminished. All design choices have trade-offs.
The portBits of a CGrafPort
In designing Color QuickDraw, certain rules were set up to provide compatibility with the new features. Here was one rule: you should be able to pass a portPixMap field to CopyBits just as you would pass a portBits field. Unfortunately, portPixMap is a handle whereas portBits is a BitMap. Tricky type-casting, however, allows one to pass in a cGrafPort's portPixMap field, as the following examples show. You must coerce the cGrafPtr in to a grafPtr in order to use the portBits field.
In C:
CGrafPtr colorPort; /* Graphics environment for color off screen */
GrafPtr savedPort; /* Pointer to the saved graphics environment */
Typecasting is not the only trickery involved here. CopyBits is faced with a major headache if you think about it. PortPixMap is a handle, and the parameters of CopyBits expect a pointer to a pixMap. To solve this, CopyBits has a little algorithm in it which dereferences the pointer twice if you pass in a portBits. To identify the type of pointer it is getting, CopyBits looks at the top two bits of the rowBytes field. As mentioned above, the top bit distinguishes between a pixMap and a bitMap. The next bit tells CopyBits if the pixMap is part of a cGrafPort or not (see Fig. 1). If the two bits are set, then CopyBits knows it is being passed a pixMapHandle, and it will perform the dereference.
Figure 1. CopyBit's BitMap parameters
Figure 1. displays three types of CopyBit's BitMap parameters. Each points to a either a bitMap, pixMap, or the portPixMap field in a cGrafPort. After 4 bytes, the top two bits of the fifth byte identifies the pointer.
Why Large PixMaps Crash Apps
As you can see, the top two bits of rowBytes have specific functions. The highest bit distinguishes between bitMaps and pixMaps. The second highest bit identifies the type of BitMap parameter. If it is set, a dereference is applied; if not, nothing happens to the pixMap. If your pixMap uses the second highest bit of rowBytes and you pass it into a QuickDraw application, it will think it is part of a cGrafPort, and will perform a handle dereference on your baseAddr. The first dereference will get to video memory, but the second dereference will be on whatever random video data happens to be there. Your application will land up in Never-Never land.
If you create pictures which can be redistributed, leave the top two bits of rowBytes alone. Below, I will identify one method of going around this limit, but it will only work in specific situations. If you read pictures, look below for reading pictures which go around this limit.
Identifying CGrafPorts and GWorlds in a Debugger
You'll notice in Figure 1 that the portVersion field of a cGrafPort coincides with the location of the rowBytes field of a grafPort. Remember, a cGrafPort has the same size as a grafPort. During debugging, you can use the same information which CopyBits uses to identify cGrafPorts.
If you use a grafPort template to display memory for an unknown grafPort, you can tell if it is a cGrafPort because the rowBytes will be equal to 0xC000. The 0xC corresponds to the two high bits being set in the portVersion field of a cGrafPort. Since these bits can not be set in a grafPort, you know you have a cGrafPort. In addition, if the bottom bit of the portVersion field is set, then it is a gWorld. Thus, if your rowBytes field has a value of 0xC001, then you know you have a gWorld.
Even or odd rowBytes?
Since the dawn of Macintosh, it has been said that rowBytes should be even because each row of a pixMap must contain an integral number of words. Actually, rowBytes has to be even because QuickDraw accesses bitmap data using word or long operands, and these generate address errors when it references an odd address on the 68000, which would happen if rowBytes is odd. The 68020 and later handle odd addresses fine, and so rowBytes can be odd. But, it is still recommended that rowBytes be even, because misaligned accesses incur a performance penalty.
Going Around The Limit Today
In the world today applications have a difficulty because they can either refuse to create pixMaps as big as users want or cause crashes by confusing CopyBits into dereferencing the base address of the pixMap if rowBytes exceeds the established limit of less than 0x4000. Some well-known image applications create such pixMaps with bad results. Reading large pixMaps causes the crash, but applications which create them are the ultimate culprit.
As far as a solution for the present, a possibility is to bypass the CopyBits dereferencing algorithm. You can call StdBits directly since it does not mind dealing with larger than legal rowBytes. The problem here is that the destination is implied and the application has to make sure that everything is alright. Also, if the destination spans multiple devices, the application has to divide the task , targeting each device at the time. See the DeviceLoop procedure in IM VI for ideas on this.
A second possibility is to patch CopyBits in situations where you know it can only be fed pixMaps. DrawPicture time is one example. You need to patch right before DrawPicture because you know a picture will contain only pixMaps. That is, you know CopyBits will not be passed portBits. If rowBytes is too big, then the application could split the job, banding the image vertically until the resulting rowBytes values fall within range. After the DrawPicture call, you will need to unpatch.
Going Around The Limit Tomorrow
All this is known by engineering and some future directions are already being studied, for example it is possible that a next release of QuickDraw will support pixMap with a rowBytes constant value indicating that the real rowBytes is contained in the planeBytes field instead; I am sure you can immediately think of cases where this is also going to cause problems but we think that the problems are less important than the limitation being overcome.
Conclusion
The limitations of rowBytes is becoming an increasingly painful thing, applications can easily create pixMaps (and PICTs) that exceed the limit of 0x4000. It is possible for an application to patch CopyBits in order to work around this limitation but the application writer has to decide what is appropriate for each set of conditions. Thus, rowBytes has traditionally been said to have a maximum value of 0x3FFE. But, if your application avoids the use of CopyBits, CopyMask, and CopyDeepMask, then you can use a rowBytes value of 0x7FFF without harm. However, those situations are rare, and, for all practical matters, the limit of rowBytes is 0x3FFE.
Further Reference:
• Inside Macintosh, Volume I, QuickDraw
• Inside Macintosh, Volume V & VI, Color QuickDraw
• d e v e l o p 1, "Realistic Color For Real-World Applications"
QD 16 - 8•24 GC QuickDraw and Deaccelerated CopyBits
QuickDraw
Written by: Guillermo A. Ortiz February 1991
This Technical Note discusses conditions that may cause _CopyBits to slow down when QuickDraw acceleration is on via the Apple 8•24 GC Display Card.
Introduction
When a drawing call is issued, GC IPC (Interprocess Communication) takes control of the call and passes it to GC QuickDraw. After the normal port set up (which involves caching the port parameters if this is the first drawing call after the port was set), GC QuickDraw returns control to the application through the IPC and performs, in parallel, the drawing to its own monitor as well as any other monitors that may be affected by the operation. The application then continues its execution, probably issuing more drawing calls that get executed in the same asynchronous manner.
The result of this mode of operation is improved performance, since the application gets control back immediately after issuing the call and the GC QuickDraw moves video data to its own video buffer as well as that of other cards in a more rapid manner by using block transfers and without requiring any action by the main processor.
_CopyBits Conforms To The Same Scheme, Except…
_CopyBits conforms to the same operational scheme, but there are some instances in which GC QuickDraw cannot perform the call in parallel; in this cases it is even possible to suffer a performance loss, since the whole call may have to be completed before control is given back to the application and GC QuickDraw has to make calls and access data across the NuBus™.
The situations that compromise GC QuickDraw parallel operation are as follows:
• When the destination device has a SearchProc installed and the source color environment is different from the destination environment.
QuickDraw calls a SearchProc whenever the source and destination have different depths and when two indexed pixel maps have different color tables, even though their depths may be identical. When GC acceleration is enabled, these conditions cause the following two types of behavior, dependent upon the source pixel map:
• If the source is an indexed pixel map, then GC QuickDraw executes the part of the setup that involves calling the SearchProc, returns control to the main processor, then completes the call in parallel. The act of calling the SearchProc before returning control makes the call slower than when no SearchProc is involved, since parallel operation does not occur throughout the whole call.
• If the source is a direct RGB pixel map, then GC QuickDraw has to call the SearchProc for every pixel that is drawn, and the application does not regain control until after the call to _CopyBits has been completed.
• When the source or destination is offscreen and not created using a GWorld.
GC QuickDraw has no way to detect when an application is going to manipulate a pixel map it has created in memory, so if it has to draw to or copy from such a PixMap, GC QuickDraw has to complete the operation before returning control to the application.
This behavior is contrary to the case when using a GWorld for offscreen environments, since in the case of a GWorld, GC QuickDraw is alerted by the call to _GetPixBaseAddr that the application is getting ready to directly change the pixels. This is the reason why it is so important that applications call _GetPixBaseAddr every time they are about to manipulate a GWorld pixel map directly.
• When the source PixMap has a color table that uses indexes that refer to a palette.
QuickDraw now allows a color table to have indexes that point to entries in the palette associated with the destination window; when bit 14 in the ctFlags field is set, the value fields in the color table are treated as palette entries. When such a PixMap is the source for _CopyBits, then GC QuickDraw has to make a number of calls to the Palette Manager as part of the setup before returning control and completing the call.
This case is similar to that of a indexed pixel map when a SearchProc is involved; therefore, it only implies a partial loss of parallelism; it is good to keep in mind that this case can only occur when the source PixMap is indexed.
Further Reference:
• Inside Macintosh, Volumes V & VI, Color QuickDraw
• d e v e l o p, “Macintosh Display Card 8•24 GC: The Naked Truth,” July 1990.
• Technical Note #275, 32-Bit QuickDraw: Version 1.2 Features
• Developer Notes for the Macintosh Display Cards 4•8, 8•24 and 8•24 GC (APDA, M085TLL/A)
NuBus is a trademark of Texas Instruments.
QD 17 - Drawing Icons
QuickDraw
Written by: Jim Friedlander October 1985
Using resources of type ICON allows drawing of icons in srcOr mode. Using resources of type ICN# allows for more variety when drawing icons.
There are two different kinds of resources that contain icons: ICON and ICN#. An ICON is a 32 by 32 bit image of an icon and can be drawn using the following Toolbox Utilities calls:
MyIconHndl:= GetIcon(iconID);
PlotIcon(destRect,iconID);
While very convenient, this method only allows the drawing of icons in SrcOr mode (as in the MiniFinder). The Finder uses resources of type ICN# to draw icons on the desktop. Because the Finder uses ICN#s, it can draw icons in a variety of ways.
An ICN# resource is a list of 32 by 32 bit images that are grouped together. Common convention has been to group two 32 by 32 bit images together in each ICN#. The first image is the actual icon, the second image is the mask for the icon. To get a handle to an ICN#, we would use something like this:
TYPE
iListHndl = ^iListPtr;
iListPtr = ^iListStruct;
iListStruct = record
icon : packed array[0..31] of Longint;
mask : packed array[0..31] of Longint;
End; {iListStruct}
VAR
myILHndl : iListHndl; {handle to an ICN#}
iBitMap : BitMap; {BitMap for the icon}
mBitMap : BitMap; {BitMap for the mask}
MyILHndl:= iListHndl(GetResource('ICN#',iconID));
if MyILHndl = NIL then HandleError;
{and exit or whatever is appropriate}
Once we have a handle to the icons, we need to set up two bitMaps that we will be using later in CopyBits:
SetRect(icnRect,0,0,32,32); { define the icon's 'bounds'}
With iBitMap do Begin
baseAddr:= @MyILHndl^^.icon;
rowbytes:= 4; { 4 * 8 =32}
bounds:= icnRect;
End; {with}
With mBitMap do Begin
baseAddr:= @MyILHndl^^.mask;
rowbytes:= 4;
bounds:= icnRect;
End; {with}
Icons can represent desktop objects that are either selected or not. Folder and volume icons can either be open or not. The object (or the volume it is on) can either be online or offline. The Finder draws icons using all permutations of open, selected and online:
Drawing icons as non-open is basically the same for online and offline volumes. We need to punch a hole in the desktop for the icon. This is analogous to punching a hole in dough with an irregular shaped cookie-cutter. We can then sprinkle jimmies* all over the cookie and they will only stick in the area that we punched out (the mask). We do this by copyBitsing the mask onto the desktop (whatever pattern) to our destRect. For non-open, non-selected icons:
we use the SrcBic mode so that we punch a white hole:
we need to do a little more work. We need to XOR a ltGray pattern into the boundsRect of the icon. We will then punch the hole, draw the icon and then XOR out the ltgray pattern that does not fall inside the mask. So, to draw the icon as offline, non-open, non-selected we would:
GetPenState(OldPen); {save the pen state so we can restore it}
PenMode(patXor);
PenPat(ltGray);
PaintRect(destRect); {paint a ltGray background for icon}
Drawing the opened icons requires one less step. We don’’t have to CopyBits the icon in, we just use the mask. Online and offline icons are drawn the same way. To draw icons as open, selected:
we do the following:
GetPenState(OldPen); {save the pen state so we can restore it}
PenMode(patXor);
PenPat(dkGray); { the icon is selected, so we need dkGray }
PaintRect(destRect); { paint a dkGray background for icon}
PaintRect(destRect); {XOR out bits outside of the mask, leaving
the mask filled with dkGray}
SetPenState(OldPen); {restore the old pen state}
To draw icons as open, non-selected:
we just need to change one line from above. Instead of XORing with a dkGray pattern, we use a ltGray pattern:
PenPat(ltGray); {icon is non-selected, so we need ltGray}
These techniques will work on any background, window-white or desktop-gray and all patterns in between. Have fun.
* jimmies : little bits of chocolate
Further Reference:
• QuickDraw
• Toolbox Utilities
QD 18 - Drawing Icons the System 7 Way
QuickDraw
Revised by: Don Moccia and C.K. Haun <TR> October 1992
Written by: Jim Mensch and David Collins October 1991
This Technical Note describes how to utilize the built-in System 7 icon drawing utility. Use this information to better conform to the System 7 visual human interface.
Changes since May 1992: In this Note, we replaced the C and Pascal interface files and corrected the related text. So much text was tweaked that change bars are used only on code changes.
Introduction
With the introduction of System 7 for the Macintosh, Apple has defined a new look and feel for many screen elements that better utilize color. Among those redefined elements are the icons drawn by the Finder and other system components. Until now, Apple has not documented how to draw icons the way the Finder does in System 7.
This Technical Note discusses the icon toolkit calls that the Finder uses to draw and manipulate the screen icons. Two of the calls, PlotIconID and PlotCIconHandle, are the ones you will probably use the most since they simply deal with drawing single icons to the screen. Some parts of the toolbox require that an icon family handle be passed to them to allow the drawing of color icons. The icon toolkit provides calls that allow you to create, draw, and manipulate these handles.
The New 'ic' Type Resources
PlotIconID and PlotCIconHandle allow the use of standard CIcons as documented in Inside Macintosh Volume V. The PlotIconID call also permits the use of a new set of icon resources documented in Inside Macintosh Volume VI, Chapter 9. This new set is a collection of different icons, representing a single Finder object, into a family. Each member of the family has the same resource ID as the 'ICN#', and a resource type indicating the icon data it contains. Currently Apple has defined three sizes of icons and three bit depths for each size. The sizes are large (32 by 32 pixels), small (16 by 16 pixels), and mini (12 by 12 pixels). The bit depths are 1, 4, and 8. The actual resource types are defined as:
Large1BitMask = 'ICN#';
Large4BitData = 'icl4';
Large8BitData = 'icl8';
Small1BitMask = 'ics#';
Small4BitData = 'ics4';
Small8BitData = 'ics8';
Mini1BitMask = 'icm#';
Mini4BitData = 'icm4';
Mini8BitData = 'icm8';
The 1-bit-per-pixel member of each size also contains the mask data for all icons of that size (yes, this means that all your icons of a certain size must have the same mask). A 1-bit-per-pixel member must exist for each icon size that PlotIconID uses. The icon size used is determined by the size of the destination rectangle. If the destination rectangle is greater than 16 pixels on a side then the large icon will be used. If the rectangle is 13–16 pixels on both sides, the small icon will be used. If it is 12 or less on each side, the mini-icon will be used. The bit depth is determined by the device of the grafPort you plot into at drawing time. Be sure to create a color grafPort when you want to use color icons.
Icon Families (or Suites and Caches As the Tool Set Refers to Them)
An icon family is simply a collection of icon handles that contain up to one image of each bit depth and size for a given icon. The family can be fully populated (every possible size or depth available), or it can have only those icons that exist or are needed. By using families, you remove the need to determine which size or depth of icon to use when drawing into a given rectangle. Several system routines, the Notification Manager for example, can take an icon family handle when an icon is requested. This permits them to use the proper color icons if available. In the case of a sparsely populated icon family, when the proper icon is not available, the icon toolkit will pick a substitute to produce the best results.
An icon cache is a family that also has a ProcPtr and a refCon. The main difference between a cache and a family is that the elements of the cache’s array are sparsely populated. When using an icon cache, the system either will use the entry in the icon family portion of the cache or, if the desired element is empty, it will call the procedure pointed to by the ProcPtr and request the data for the icon. The procedure should have this interface:
FUNCTION IconGetter(theType: ResType;
yourDataPtr: Ptr): Handle;
This function should return either the icon data to be drawn or NIL to signify that this entry in the icon cache does not exist. Icon caches can be used with all icon family calls. A few extra calls are also available to manipulate icon caches.
Drawing Modes or Transforms
In addition to various sizes and bit depths, icons can be drawn with different modes or transforms. Transforms are analogous to certain Finder states for the icons. For example, the transform that you would use to show an icon of a disk that has been ejected is ttOffline. Here is a list of the available transforms:
ttNone = $0;
ttDisabled = $1;
ttOffline = $2;
ttOpen = $3;
ttSelected = $4000;
ttSelectedDisabled = (ttSelected + ttDisabled);
ttSelectedOffline = (ttSelected + ttOffline);
ttSelectedOpen = (ttSelected + ttOpen);
The actual appearance of the icon drawn by each transform type may vary with future system software, so you should always use the transform that best fits the state it represents in your application. This way you will be consistent with future changes to the look and feel of regular system icons. Note the ttSelected transform can be added to any of the other transform types.
There are also transforms that use the Finder label colors to color the icon. To determine the proper label for a file’s icon, you can check bits 1–3 of the fdFlags field in the file’s Finder info. (See the Finder Interface chapter in Inside Macintosh Volume VI for more information). These bits contain a number from 0 to 7. Simply add the corresponding ttLabel value to the transform that you give the call. The label values are defined like this:
ttLabel1 = $0100;
ttLabel2 = $0200;
ttLabel3 = $0300;
ttLabel4 = $0400;
ttLabel5 = $0500;
ttLabel6 = $0600;
ttLabel7 = $0700;
Alignment
Most icons do not fully fill their rectangle, and it is sometimes necessary to draw an icon relative to other data (like menu text). In these instances it is nice to be able to have the icon move in its rectangle so that it will be at a predictable location in the destination rectangle. When drawing an icon you can pass one of these standard alignments in the alignment parameter or you can add a vertical alignment to a horizontal alignment to create a composite alignment value.
And Now (Drum Roll Please) the Calls and What to Pass
Now that we have defined every major data type we can think of, here are the actual toolkit calls themselves. One word of caution: only the ForEachIconDo call protects the handle that is passed to it, so make your icon resources nonpurgeable.
Icon Family Calls
FUNCTION NewIconSuite(VAR theIconSuite: Handle): OSErr;
NewIconSuite returns an empty icon family handle with all members set to NIL.
FUNCTION AddIconToSuite(theIconData: Handle;
theSuite: Handle;
theType: ResType): OSErr;
This call will add the data in theIconData into the suite at the location reserved for theType of icon data. AddIconToSuite will replace any old data in that slot without disposing of it, so you may want to call GetIconFromSuite to obtain the old handle (if any) to dispose of it. This call will be used most often with the NewIconSuite call to fill the empty family after it’s created.
FUNCTION GetIconFromSuite(VAR theIconData: Handle;
theSuite: Handle;
theType: ResType): OSErr;
This call will return a handle to the pixel data of the family member of theSuite specified by theType. If you intend to dispose of this handle, be sure to call AddIconToSuite with a NIL handle to zero out the family entry.
FUNCTION ForEachIconDo(theSuite: Handle;
selector: IconSelectorValue;
action: IconAction;
yourDataPtr: Ptr): OSErr;
This routine will call your IconAction procedure (see below) for each icon in the family specified by selector and theSuite. The selector parameter is a bit-level flag that specifies which family members to operate on; they can be added together to create composite selectors that work on several different family members. The values for selector are:
The action procedure that gets called for each icon type selected for the family is a Pascal type function with the following interface:
FUNCTION IconAction(theType: ResType;
VAR theIcon: Handle;
yourDataPtr: Ptr): OSErr;
The parameter theIcon is passed by reference here so that your routine can modify the contents of the suite directly. The yourDataPtr parameter is the value passed when you called ForEachIconDo. It allows you to easily communicate with your application. The action procedure returns an OSErr. If any value other than noErr is returned, ForEachIconDo will stop processing immediately and return the error passed. (Note: This implies that the icons selected may only be partially operated on. There is no guaranteed order in which the icons get operated on.)
FUNCTION GetIconSuite(VAR theIconSuite: Handle;
theResID: INTEGER;
selector: IconSelectorValue): OSErr;
GetIconSuite will create a new icon family and fill it from the current resource chain with the icons of resource ID theResID and types indicated by selector. This is the call you will probably use most often to create an icon family. Note that if you SetResLoad(False) before making this call, the suite will be filled with unloaded resource handles.
FUNCTION PlotIconSuite(theRect: Rect;
align: IconAlignmentType;
transform: IconTransformType;
theIconSuite: Handle): OSErr;
This call renders the proper icon image from the passed icon family based on the bit depth of
the display you are using and the rectangle that you have passed. The parameters align and transform are applied to the icon selected for drawing and then the icon is plotted into the current grafPort. PlotIconSuite chooses the appropriate icon based primarily on size. Once the proper icon size is determined (based on the destination rectangle), the present member of that size with the deepest bit depth that the current device can use is selected. A size category is considered present if the black-and-white member (with mask), 'ICN#', 'ics#', or 'icm#', is present. PlotIconSuite can be used for both picture accumulation and printing.
FUNCTION DisposeIconSuite(theIconSuite: Handle;
disposeData: BOOLEAN): OSErr;
This call disposes the icon family handle itself. In addition, if disposeData is true, any of the icon data handles that do not belong to a resource fork will also be disposed.
FUNCTION SetSuiteLabel(theSuite: Handle; theLabel: INTEGER): OSErr;
This call allows you to specify a label to draw an icon of this suite when no label is specified in PlotIconSuite. This is used primarily to ensure that a family passed to a system routine gets drawn with the proper label. The default label can be overridden by specifying a label in PlotIconSuite.
FUNCTION GetSuiteLabel(theSuite: Handle): INTEGER;
GetSuiteLabel returns the label previously set with SetSuiteLabel.
Icon Cache Calls
In addition to the icon family calls, icon caches have these additional calls:
FUNCTION MakeIconCache(VAR theHandle: Handle;
makeIcon: IconGetter;
yourDataPtr: UNIV Ptr): OSErr;
This call creates an empty icon cache similar to NewIconSuite, and associates the additional icon loading procedure and data value with the family.
FUNCTION LoadIconCache(theRect: Rect;
align: IconAlignmentType;
transform: IconTransformType;
theIconCache: Handle): OSErr;
This call allows preflight loading of certain elements of your icon cache. This is handy if you suspect that certain drawing operations may occur at a time not convenient for loading your icon data (e.g., when your resource fork might not be in open chain). LoadIconCache takes the same parameters as PlotIconSuite and uses the same criteria to select the icon to load. The grafPort must be set properly before making this call since it is one of the criteria for determining the icon to load.
The following four calls are provided to change theData or theProc associated with an icon cache:
FUNCTION GetIconCacheData(theCache: Handle;
VAR theData: Ptr): OSErr;
FUNCTION SetIconCacheData(theCache: Handle;
theData: Ptr): OSErr;
FUNCTION GetIconCacheProc(theCache: Handle;
VAR theProc: IconGetter): OSErr;
FUNCTION SetIconCacheProc(theCache: Handle;
theProc: IconGetter): OSErr;
Plotting Icons Not Part of a Suite
The next calls are grouped because they are similar. They let you plot an icon to the screen without your creating an icon suite. They are also good if you have a 'cicn' instead of an icon family.
FUNCTION PlotIconID(theRect: Rect;
align: IconAlignmentType;
transform: IconTransformType;
theResID: INTEGER): OSErr;
FUNCTION PlotCIconHandle(theRect: Rect;
align: IconAlignmentType;
transform: IconTransformType;
theCIcon: CIconHandle): OSErr;
FUNCTION PlotIconMethod(theRect: Rect;
align: IconAlignmentType;
transform: IconTransformType;
theMethod: IconGetter;
yourDataPtr: UNIV Ptr): OSErr;
FUNCTION PlotIconHandle(theRect: Rect;
align: IconAlignmentType;
transform: IconTransformType;
theIcon: Handle): OSErr;
FUNCTION PlotSICNHandle(theRect: Rect;
align: IconAlignmentType;
transform: IconTransformType;
theSICN: Handle): OSErr;
All these routines share the following parameters: theRect is the destination rectangle to draw the indicated icon into; align is the alignment method to use if the icon does not fit the rectangle given; transform indicates the desired appearance of the icon on the screen.
In PlotIconID, the parameter theResID is the resource ID of the family of 'ic' type resources to use. If the correct bit depth or the size required is not defined, the closest-fitting one will be used.
The PlotCIconHandle parameter theCIcon is a handle that you get to a standard QuickDraw color icon. Unlike PlotCIcon, PlotCIconHandle does not honor the current foreground and background colors. Call GetCIcon to load the icon. Dispose of it when you are done, since they can take up quite a bit of memory.
PlotIconMethod calls your IconGetter procedure, discussed earlier, to check for the existence of icon data.
PlotIconHandle will plot the data from an 'ICN#' or 'ICON' resource from its handle. It is a new version of PlotIcon.
PlotSICNHandle plots the data of a 'SICN' resource from its handle. Only 'SICN' resources with a single member, or one in which the second member is a mask for the first, will plot correctly.
All the functions return an error code if things did not go well with the drawing or, in the case of the PlotIconID call, if the indicated icon family could not be used.
Miscellaneous Calls
FUNCTION GetLabel(labelNumber: INTEGER;
VAR labelColor: RGBColor;
VAR labelString: Str255): OSErr;
This call returns the actual color and string used in the label menu of the Finder and the label’s Control Panel. This information is provided in case you wish to include the label text or color when displaying a file’s icon in your application.
FUNCTION IconSuiteToRgn(theRgn: RgnHandle;
iconRect: Rect;
align: IconAlignmentType;
theIconSuite: Handle): OSErr;
FUNCTION IconIDToRgn(theRgn: RgnHandle;
iconRect: Rect;
align: IconAlignmentType;
iconID: INTEGER): OSErr;
FUNCTION IconMethodToRgn(theRgn: RgnHandle;
iconRect: Rect;
align: IconAlignmentType;
theMethod: IconGetter;
yourDataPtr: Ptr): OSErr;
These routines will create a region from the mask of the icon selected by the iconRect and align values passed. They will allow you to do accurate hit testing and outline dragging of an icon in your application. The RgnHandle must have been previously allocated before you make this call.
FUNCTION RectInIconSuite(testRect: Rect;
iconRect: Rect;
align: IconAlignmentType;
theIconSuite: Handle): BOOLEAN;
FUNCTION RectInIconID(testRect: Rect;
iconRect: Rect;
align: IconAlignmentType;
iconID: INTEGER): BOOLEAN;
FUNCTION RectInIconMethod(testRect: Rect;
iconRect: Rect;
align: IconAlignmentType;
theMethod: IconGetter;
yourDataPtr: Ptr): BOOLEAN;
FUNCTION PtInIconSuite(testPt: Point;
iconRect: Rect;
align: IconAlignmentType;
theIconSuite: Handle): BOOLEAN;
FUNCTION PtInIconID(testPt: Point;
iconRect: Rect;
align: IconAlignmentType;
iconID: INTEGER): BOOLEAN;
FUNCTION PtInIconMethod(testPt: Point;
iconRect: Rect;
align: IconAlignmentType;
theMethod: IconGetter;
yourDataPtr: Ptr): BOOLEAN;
These calls hit test the passed Point or Rect against the icon indicated. The parameters iconRect and align, and the grafPort should be the same as when the icon was last drawn. The functions return true if the point is in the icon mask or if the rectangle intersects the icon mask.
Error Codes
The Icon Utilities will pass back any errors encountered during execution, so you can expect to see Memory Manager, Resource Manager, and other normal errors.
There is one error code defined specifically for the Icon Utilities routines that may be returned by the Icon plotting routines.
{ Pascal }
CONST
noMaskFound = -1000;
END;
/* C */
#define noMaskFound -1000
This error will be returned if the Icon Utilities package could not find or create a mask for the icon family. The Icon Utilities will use the correct mask for each icon size, if one is available. If no mask for a specific size is available, a mask will be created from any mask in the family, but if there are no 1 bit images and no mask in the family, plotting calls will fail with this error.
Type(def)s and Glue for Pascal and C
The Pascal and C interfaces are provided here to copy and paste since the current MPW standard interface files do not contain the glue for these calls. Electronic versions of this note are on AppleLink (Developer Support: Developer Services: Technical Documentation: Macintosh Technical Notes: Imaging: Graphics: Icon Drawing in 7.sit (Stuffit)) and the Developer CD (Technical Documentation: Macintosh Technical Notes: Imaging: Graphics: Icon Drawing in 7).
MPW C, Pascal, and Assembler files also have been submitted to AppleLink and the Developer CD, but their paths were not known when this note was written.
• Inside Macintosh, Volume VI, Finder Interface chapter
QD 19 - Fixed CLUT Devices and the Single Techno Nerd
QuickDraw
Written by: Guillermo A. Ortiz December 1992
Applications that need a given set of colors to look good or make use of color table animation can obtain undesired results in PowerBook 160, PowerBook 180, and PowerBook Duo Macintosh models. The reason for this is that these models sport Fixed Color Devices associated with the flat screen display. This Tech Note describes what a Fixed CLUT device is and presents some solutions to the challenges presented by this display type.
Topics
• Fixed CLUT video devices
• Color table animation and Fixed CLUT devices
• Different GDevice types and their implications
Introduction
Since the introduction of the Macintosh II, Inside Macintosh Volume V defined three types of graphics devices:
Indexed CLUT devices ( gdType = 0 )
Fixed CLUT devices ( gdType = 1 )
Direct RGB devices ( gdType = 2 )
In the case of indexed devices, values that range from 0 to 2n – 1 (where n is the depth of the device under consideration) point to entries in a table of RGB triplets, thus determining the color associated with a given pixel value. In the case of direct RGB devices, the pixel value is in itself an RGB triplet, therefore defining the color associated with the pixel.
Up to recently, the only type of indexed CLUT devices known to humanity has been gdType = 0 or Indexed CLUT devices; these are the devices that drive most display boards when set to depths between 1 and 8 (2 colors to 256 colors). As far as color is concerned, the main feature of indexed devices is that the color table for a given device can be changed either directly by using SetEntries or indirectly when the Palette Manager establishes the color environment in response to a palette associated with the frontmost window (if it has one.)
Fixed CLUT devices enter with the introduction of the PowerBook Duo, PowerBook 160, and PowerBook 180 portables. All these machines support 16-color gray displays (depth = 4); the trick is that although the video does have a color table, it can not be changed at all. What this means is that no color table animation is possible and that the palette manager can not give you the colors you want or affect palette animation.
Checking for the type of device is really simple. Once you have the device handle all you have to write is something like:
DisplayInfo.gdType = (*currDev) -> gdType;
Naturally, once the application knows it has to deal with a Fixed CLUT device, it may want to do something based on this information.
So What!
At this point the smart reader is probably starting to wonder what kind of help this Tech Note is going to provide as far as not having to stay alone at home this Friday evening: Sadly enough the answer is none. Nevertheless it can help avoid support calls from PowerBook users when their favorite application starts acting weird.
Now, the first thing to emphasize is that most developers don’t have to worry about this—it is not as if your word processing application is going to start crashing. However, applications that by their nature have to modify the color environment to run properly have to take some precautions.
We will discuss some scenarios and the implications for each one. Other possibilities may exist, but these will help you understand the issues.
• Non-Color-Intensive Applications Need Not to Worry
Applications that deal with text and black-and-white stuff should not be concerned. Even applications that provide limited color capabilities will have no problem.
• Color-Enhanced Applications
Certain applications such as games and presentation packages require a given set of colors to look “cool.” Probably the Palette Manager is used in order to assure that the minimal set of colors is present. Applications like these need to make sure there is a fallback similar to the one used when the application is running in a black-and-white environment. Note that prompting the user to change the depth won’t do much good.
• Color-Intensive Applications
Applications that require heavy manipulation of the color environment to do their stuff need to be ready to take the path taken when the machine the user is on has no color or can not be set to the optimum depth. Applications that call the video driver directly such as those that animate colors by calling the _Control routine especially need to make sure the device in question is capable of color table changing; the control call returns an error -17 (controlErr - Driver can not respond to Control call) if SetEntries or DirectSetEntries is tried.
Note 1: The PowerBook Duo can be docked to a number of different docking stations, the currently available docking stations are:
a. Duo Dock. In this case the flat display is not used at all and the main device becomes an Indexed CLUT device with depths depending on the monitor being used.
b. Mini Dock. The main display is still in use and the monitor configuration for the machine becomes a two-display system with one Indexed CLUT device (external display) and a Fixed CLUT device (flat display).
Note 2: PowerBook Duo, PowerBook 160, and PowerBook 180 Macintosh computers can be configured in a display mode called Mirroring using the PowerBook Display control panel. In this configuration the system sees two devices that share the same bounds. The result is that drawing to the main device results in drawing into both screens with QuickDraw taking care of the details such as different depths and color/black-and-white settings.
In Conclusion
Fixed CLUT devices add a little twist to the things that an application developer has to be aware of. With the popularity of the PowerBook line, it becomes really important that applications know how to configure in this case in order to continue providing smooth performance under the varied conditions now existent in the Macintosh family of computers.
Further Reference:
• Inside Macintosh, Volume VI, The Graphics Devices Manager
• Macintosh Duo System Developer Note (Available on the Developer CD)
• Macintosh PowerBook 160 and Macintosh PowerBook 180 Developer Note (Available on the Developer CD)
QD 20 - _PackBits : Things You Wanted to Know About* *But Were Afraid to Ask
QuickDraw
Revised by: Guillermo Ortiz, Jon Zap, and Forrest Tanaka January 1992
Written by: Cameron Birse November 1987
This Technical Note describes the format of data packed by the Toolbox utility _PackBits and documents a change to the srcBytes limit and possible worst case. Although you can simply unpack this data using _UnPackBits, Apple provides this information for the terminally curious and for those manipulating MacPaint® documents or PICT files by hand.
Warning: This format information is subject to change.
Changes since November 1990: A warning has been added about the handling of a flag-counter byte value of -128.
Length Doesn’t Matter
Inside Macintosh, Volume I-470, describes the Pascal interface to the _PackBits trap as follows:
The accompanying text states that srcBytes, the length of your uncompressed data, should not be greater than 127, and that in the worst case, the compressed data can be srcBytes + 1. To pack more than 127 bytes, you had to break the data up into 127-byte groups and call _PackBits on each group. Beginning with system software version 6.0.2, this limit of 127 bytes is no longer valid. The new limit is 32,767 bytes, which is the maximum positive number that srcBytes can hold. The worst case can be determined according to the following formula:
(srcBytes + (srcBytes+126) DIV 127)
which is comparable to what you would get if you broke up the data into 127-byte groups and picked up an additional byte for each group.
Mommy, How Do They Make Packed Bits?
The first byte is a flag-counter byte that specifies whether or not the the following data is packed, and the number of bytes involved. If this first byte is a negative number, then the following data is packed. In this case, the number is the two’s complement of a zero-based count of the number of times the data byte repeats when expanded. There is one data byte following this first byte in packed data. The byte after the data byte is the next flag-counter byte.
If the flag-counter byte is a positive number, then the following data is unpacked. In this case, the number is a zero-based count of the number of incompressible data bytes that follow. There are (flag-counter+1) data bytes following the flag-counter byte. The byte after the last data byte is the next flag-counter byte.
Note that there is no way to know, given a pointer to the start of packed data, when you have reached the end of the packed data. This is why you need to know either the length of the packed or unpacked data before you start unpacking. _UnPackBits requires the length of the unpacked data.
Warning: _PackBits never generates the value -128 ($80) as a flag-counter byte, but a few PackBits-like routines that are built into some applications do. _UnpackBits handles this situation by skipping any flag-counter byte with this value and interpreting the next byte as the next flag-counter byte. If you’re writing your own UnpackBits-like routine, make sure it handles this situation in the same way.
Consider the following example:
Unpacked data:
AA AA AA 80 00 2A AA AA AA AA 80 00 2A 22 AA AA AA AA AA AA AA AA AA AA
After being packed by _PackBits:
FE AA ; (-(-2)+1) = 3 bytes of the pattern $AA
02 80 00 2A ; (2)+1 = 3 bytes of discrete data
FD AA ; (-(-3)+1) = 4 bytes of the pattern $AA
03 80 00 2A 22 ; (3)+1 = 4 bytes of discrete data
F7 AA ; (-(-9)+1) = 10 bytes of the pattern $AA
or
FE AA 02 80 00 2A FD AA 03 80 00 2A 22 F7 AA
* * * * *
The bytes with the asterisk (*) under them are the flag-counter bytes. _PackBits packs the data only when there are three or more consecutive bytes with the same data; otherwise it just copies the data byte for byte (and adds the count byte).
Note: The data associated with some PICT opcodes, $0098 (PackBitsRect) and $0099 (PackBitsRgn), contain PixData which is basically made of _PackBits data. It should be noted, though, that the format for PixData includes a byteCount or length in addition to the data described in this Note.
For example, the following is the result of decoding a sample PICT2:
/* Now we have the scan line data packed as follows:
[bytecount for current scan line] [data as defined above]
If rowBytes is > 250 then byteCount is a word else is a byte
(in this case, byteCount is a byte)
note that each unpacked row adds to 30 rowBytes
*/
/* line 1, byte count is 2 (best case for a row) */
02
E3 FF /* -(-29) + 1 = 30 FF's */
/* line 2, byte count is 19 (0x13) */
13
01 FF 23 /* 1+1 data bytes */
FE 00 /* -(-2)+1 0's */
FC 23 /* -(-4)+1 0x23's */
FE 00 /* 3 0's */
FC 23 /* 5 0x23's */
FE 00 /* 3 0's */
FC 23 /* 5 0x23's */
FE 00 /* 3 0's */
00 FF /* 1 data byte */
/* line 3, byte count is 28 */
1C
02 FF 00 23 /* 3 data bytes */
FE 00 /* 3 0's */
FE 23 /* 3 0x23's */
01 00 23 /* 2 data bytes */
FE 00 /* 3 0's */
FE 23 /* 3 0x23's */
01 00 23 /* 2 data bytes */
FE 00 /* 3 0's */
FE 23 /* 3 0x23's */
04 00 23 00 00 FF /* 5 data bytes */
/* line 4, byte count is 31 (worst case for a row) */
1F
03 FF 00 00 23 /* 4 data bytes */
FE 00 /* 3 0's */
00 23 /* 1 data byte */
FE 00 /* 3 0's */
00 23 /* 1 data byte */
FE 00 /* 3 0's */
00 23 /* 1 data byte */
FE 00 /* 3 0's */
00 23 /* 1 data byte */
FE 00 /* 3 0's */
00 23 /* 1 data byte */
FE 00 /* 3 0's */
02 23 00 FF /* 3 data bytes */
/* line 5, byte count is 28 */
1C
01 FF 00 /* 2 data bytes */
FE 23 /* 3 0x23's */
01 00 23 /* 2 data bytes */
FE 00 /* 3 0's */
FE 23 /* 3 0x23's */
01 00 23 /* 2 data bytes */
FE 00 /* 3 0's */
FE 23 /* 3 0x23's */
01 00 23 /* 2 data bytes */
FE 00 /* 3 0's */
FE 23 /* 3 0x23's */
00 FF /* 1 data byte */
/* line 6, byte count is 18 */
12
00 FF /* 1 data byte */
FC 23 /* 5 0x23's */
FE 00 /* 3 0's */
FC 23 /* 5 0x23's */
FE 00 /* 3 0's */
FC 23 /* 5 0x23's */
FE 00 /* 3 0's */
FD 23 /* 4 0x23's */
00 FF /* 1 data byte */
/* line 7, byte count is 2 (best case for a row) */
02
E3 FF /* 30 0xFF's */
00 /* pad so next command starts at word boundary */
00FF /*end of pic */
};
Further Reference:
• Inside Macintosh, Volume I-465, The Toolbox Utilities
• Inside Macintosh, Volume V-39, Color QuickDraw
• Technical Note M.PT.MacPaintDoc —
MacPaint Document Format
MacPaint is a registered trademark of Claris Corporation.
QD 21 - Of Time and Space and _CopyBits
QuickDraw
Written by: Forrest Tanaka June 1990
This Technical Note describes the various factors that can influence the speed of _CopyBits so that developers can set up conditions to achieve the best performance for the particular situation.
Can You Influence the Speed of _CopyBits?
_CopyBits has never been an “easy” QuickDraw routine, like _LineTo or even _OpenPort. Most programmers who are just beginning to adjust themselves to the Macintosh usually have to give _CopyBits a few tries before the right bits copy to the right places. Even many who feel that they have become Macintosh programmers still see reflections in their monitors of furrows between their eyebrows as they begin to press the key labelled “C.”
_CopyBits is one of those routines that is so full of subtlety, it has the beginnings of something that could be considered to be personality. One subtlety involves the second most important thought that’s on the minds of any computer programmer: execution speed. Why is _CopyBits fast? Why is it slow? Can I influence its speed? Is there really a clandestine state of reason? Is there a price to speed?
Influences on the Speed of _CopyBits
Yes, you can influence the speed of _CopyBits. Yes, it’s even predictable. And yes, it’s possible that you have to compromise to get the maximum speed. This Note is intended to give you a deeper understanding of the ways that the speed of _CopyBits can be affected; and hopefully you can then set up conditions for a _CopyBits call without the disturbing notion that someone else might be doing the same thing just a little bit better than you.
This Note talks about every factor that affects the speed of _CopyBits that I can think of and that can be reasonably controlled by a programmer or the person using an application. There are other factors not mentioned in this Note because I felt that they were just too esoteric to describe with any meaning.
In each case, this Note tries to give real-life examples showing the effect of each factor. These examples are just to give you a relative idea of the importance of each effect. In real life, the effects of the different factors give results that could be a lot different from the results presented in this Note. Each example is based on 100 _CopyBits calls from an off-screen pixel map to the screen on a Macintosh IIcx with an Apple Extended Video Card which is running System Software 6.0.5 and 32-Bit QuickDraw 1.2. The off-screen pixel map is eight bits deep with the standard eight-bit color table and 256 pixels high by 256 pixels wide. The screen is also in eight-bit color mode. Calling _CopyBits to copy the entire off-screen pixel map to the screen 100 times takes 204 ticks, and this Note refers to this figure as the “standard test.” Since a tick on a Macintosh is approximately 1/60 of a second, the standard test runs at slightly less than 30 frames per second. As this Note discusses each factor, it presents an example with that factor changing and all other factors remaining the same as the standard test, which allows you to compare performance of the changed factor to that of the standard test of 204 ticks.
What follows is a discussion of each factor that can influence the speed of _CopyBits, in no particular order.
Dimensions of the Copied Area
One of the most obvious factors has to do with the dimensions of the copied area. _CopyBits takes as parameters two rectangles which specify the portion of the source pixel map from which you want to copy and the portion of the destination pixel map to which you want to copy it. All other factors being equal, the larger the rectangles, the more pixels _CopyBits has to copy and the longer it takes to do the job. To keep _CopyBits as fast as possible, copy the smallest rectangle possible.
Modifying the standard test so that _CopyBits only copies a 128-pixel wide by 128-pixel tall area produces a result of 109 ticks, which compares to the 204 tick performance for a 256-pixel wide by 256-pixel tall area.
QuickDraw is usually faster drawing wide things than it is drawing tall things, because consecutive pixels in memory are displayed horizontally. Drawing a series of pixels that are next to each other horizontally is easy because QuickDraw simply has to set consecutive memory locations, while drawing a series of pixels that are next to each other vertically is just a little bit harder because the address of each pixel must be calculated. _CopyBits is no exception to this general rule; it copies a row of pixels, goes to the next row, copies that row, goes to the next row, and so on. The time spent going between rows is a lot more than the time going between pixels on one row, so the effect is that _CopyBits is faster copying a short and wide section of a pixel map than it is copying a tall and narrow one. To keep _CopyBits as fast as possible, copy the shortest rectangle possible.
Modifying the standard test again so that the source and destination rectangles are 256 pixels wide by 50 pixels tall produces a result of 110 ticks, while modifying it so that the source and destination rectangles are 50 pixels wide by 256 pixels tall results in a time of 123 ticks. These 13 ticks may not seem like a big deal, but combined with other factors, there may be a case where they make a big difference.
Shape and Size of the Clip, Visible, and Mask Regions
_CopyBits always makes sure that it stays within the lines, so to speak. _CopyBits copies pixels clipped to the maskRgn that you pass as the last parameter to the call. If the destination is the current GrafPort, _CopyBits additionally clips to a region that’s the intersection of the clipRgn and visRgn of the port. If the intersection of these three regions is not rectangular, then _CopyBits has to check each pixel to make sure it falls within the intersection, and this check slows _CopyBits down. If the intersection of these three regions is rectangular, then _CopyBits takes the fast case of copying constant-sized rows. To keep _CopyBits as fast as possible, make sure the intersection of the clipRgn and visRgn of the destination GrafPort and the maskRgn is rectangular. Of course, if the destination GrafPort is a window, then the visRgn is under the user’s control.
In general, if the region that you are copying into has straight vertical edges for the most part, the time penalty of using a non-rectangular region is not that bad. Regions that only have small portions that are straight and vertical are the ones that slow _CopyBits down in a big way. Regions that are twisted or that have holes or islands can also have a big effect upon the speed, depending upon how complicated they are. As a rule of thumb, if a region looks like it slows _CopyBits, it probably does.
Modifying the standard test so the maskRgn is set to a circle that inscribes the example pixel map results in a time of 303 ticks, which is considerably longer than the standard test result of 204 ticks that involved copying a much larger area. Modifying the maskRgn to a square with 226 pixels per side, which has about the same total area of the circle just used, results in a time of 176 ticks.
Transfer Modes
Macintoshes without Color QuickDraw have eight transfer modes that work with _CopyBits, while those Macintoshes with Color QuickDraw get an additional nine modes. Because the algorithms for each of these modes can be pretty different from the others, the time it takes _CopyBits to work with each of these modes can vary radically. For several of these modes, the speed of _CopyBits can vary a lot depending upon the particular image being copied and the image over which this image is copied. It can also vary non-linearly depending upon the depth of the pixel maps. The arithmetic modes in particular are highly optimized for 32-bit deep pixel maps.
The standard test copies a fairly average-looking ray-traced image to a white background. Modifying the standard test to erase the background between each of the 100 calls to _CopyBits produced the following results for the modes listed (the tests were obviously also changed to reflect the proper mode. In addition, to make the results a little more meaningful, the time it took to erase the background has been subtracted from each result.
Of course, the amount of time taken by some of these modes can be changed by changing the image to copy and the image over which it is copied. These figures are just to give an idea of how fast or slow some of these modes are in this particular situation.
There is actually one more mode which is not mentioned: ditherCopy. Apple introduced this mode with 32-Bit QuickDraw, and it makes _CopyBits do error-diffusion dithering when copying a pixel map from one depth to a pixel map of a lesser depth or to a pixel map of the same depth with a different color table. The speed of this transfer mode can be very fast or very slow, depending upon what pixel depths and colors are used and the particular image being copied. The ditherCopy mode is not included in the table since the range of figures is potentially very large; play with it and see for yourself. For more information about this mode, refer to the Color QuickDraw chapter in Inside Macintosh, Volume VI and the 32-Bit QuickDraw Developers’ Notes from APDA.
Colorization
There is a variation of _CopyBits if the destination pixel map is the current port and the foreground color is not black or the background color is not white. If this is the case, then the source image is colorized when it’s copied. For details, see Technical Note #163, Adding Color with _CopyBits. Because this colorization requires extra processing, _CopyBits slows down. To keep _CopyBits as fast as possible, make sure the foreground color is black, the background color is white, and that the current GDevice pixel map’s color table has white in the first position and black in the last position.
Modifying the standard test so that the foreground color is pure red and the background color pure blue produces a result of 579 ticks.
Pixel Alignment
The alignment of pixels in the source pixel map relative to their alignment the destination pixel map can be surprisingly important to the speed of _CopyBits, but what is pixel alignment? Following is an example to demonstrate the concept of pixel alignment. Imagine you want to perform a _CopyBits on a one-bit-per-pixel off-screen pixel map into a window on a one-bit-per-pixel screen, and the window is three pixels from the left edge of the screen.
If you copy the entire off-screen pixel map to the left edge of the window, then _CopyBits must realign the pixels. Since the leftmost pixels of the off-screen pixel map are on a byte boundary, but the left edge of the window is three pixels away from a byte boundary, _CopyBits has to shift (or realign) each byte from the off-screen pixel map by three pixels before placing it on the screen. The process of aligning the pixels slows down _CopyBits.
Figure 1 shows an example of this realignment. An off-screen bit map specified by a pointer to a BitMap called offScreen is being copied to a window specified by a WindowPtr called window. window, which is 256 pixels wide and 256 pixels high, is positioned 50 pixels from the top of the screen and three pixels from the left edge of the screen. The screen has 512 pixels horizontally and 342 pixels vertically. The source rectangle that is passed to _CopyBits is sourceRect and the destination rectangle is destinationRect. Because offScreen is misaligned by three pixels, _CopyBits has to shift offScreen by three pixels before placing the image on the screen.
Figure 1–offscreen Needs Realignment
By adjusting the off-screen pixel map so that its leftmost pixels are also three pixels away from a byte boundary, _CopyBits can just copy the bytes without shifting, which is a lot faster. This example holds true on all Macintosh models, whether they have Color QuickDraw or not. To keep _CopyBits as fast as possible, make sure the pixels in memory are aligned with the pixels on the screen. Figure 2 shows the same situation as Figure 1, except that offScreen is now properly aligned to window.
Figure 2–offscreen Aligned
Many, if not most, Color QuickDraw Macintoshes have video cards that can display one pixel per byte, so one would think that pixel alignment does not apply in these cases, since all pixels are at byte boundaries. This statement is true enough, but there is still another kind of alignment that should be done on these machines. Macintoshes with Color QuickDraw generally have full 32-bit microprocessors, and these microprocessors are at their fastest when they can transfer long words aligned on long-word boundaries in memory.
Modifying the last example so that the off-screen pixel map and the screen are both eight-bits-per-pixel, the pixel at the extreme top left corner of the off-screen pixel map is located at a long-word boundary, because the Macintosh Memory Manager forces it to be located there; however, the pixel at the extreme top left corner of the window is located three bytes away from the previous long-word boundary. No bit shifting is needed, because each pixel takes up a whole byte, but _CopyBits does have to take the non-optimum case of copying long words on non-long-word boundaries. This case works fine, but it is not quite as fast as it could be. To keep _CopyBits as fast as possible, make sure pixels in the source and destination pixel maps are aligned on long-word boundaries.
Since 1984, Macintosh programmers have been told that rowBytes must be even. That is still true, but to allow _CopyBits to copy an entire pixel map on long-word boundaries, rowBytes must be a multiple of four so that every line in a pixel map begins on a long-word boundary. The following formula can be used to find the minimum rowBytes needed for a pixel map’s bounds rectangle with right and left coordinates of bounds.right and bounds.left, and a pixel depth of pixelDepth:
Off-screen GWorld support, which was introduced with 32-Bit QuickDraw, can automatically set up a pixel map so that it’s properly aligned to any part of the destination pixel map or bit map. You can specify that you want this by passing zero for the pixel depth and passing the rectangle of the destination area in global coordinates. See the 32-Bit QuickDraw Developers’ Notes and “Braving Offscreen Worlds” in d e v e l o p, January 1990 for details.
The way that _NewGWorld aligns a GWorld is to set up the off-screen pixel map so that its rowBytes is four bytes wider than one would normally calculate. Four bytes is the maximum amount that any pixel map would have to be realigned at any pixel depth. The bounds rectangle’s left coordinate is set to the negative of the left coordinate of the destination rectangle in global coordinates modulo (32 / pixel depth), because this is maximum amount that a pixel map must be shifted to achieve perfect alignment. To build on the earlier example, assume you have a 128-pixel wide, eight-bit deep, off-screen pixel map to copy to a window that is three pixels away from the left edge of an eight-bit color screen.
First, the rowBytes for the off-screen pixel map is set to 131 to allow room for realignment. To align the off-screen pixel map to the on-screen window, the left coordinate of the off-screen bit map’s bounds is set to -3 and the right coordinate is still at 128. Notice that the off-screen pixel map’s bounds is now 131 pixels wide. Now, the pixels in the off-screen pixel map with a horizontal coordinate of 0 are located three bytes away from the previous long-word boundary. The pixels on the left edge of the window are also located three bytes away from the previous long-word boundary, so _CopyBits can copy long words on long-word boundaries.
If a user moves the window so that it’s two pixels from the left edge of the screen, the off-screen pixel map must be realigned. _UpdateGWorld is used to do this. It changes the left coordinate of the off-screen pixel map’s bounds rectangle to -2 and then it shifts all the pixels in the off-screen pixel map one pixel to the left. The extra four bytes in each row provide the room for this shifting. (Gives you some new respect for the off-screen support, doesn’t it?)
This same discussion applies to any pixel depth, though shallower pixel depths require bit shifting rather than byte shifting. The same principles apply, though. Notice that in a 32-bit deep pixel map, all pixels are aligned on long-word boundaries, so no bit shifting or byte shifting ever needs to be done on one of those. _NewGWorld still adds four to rowBytes even in this case, however.
Modifying the standard test so that the source and destination pixel maps are four bits deep with perfect pixel alignment produces a result of 78 ticks; however, if the destination pixel map is one pixel left of perfect alignment, the result is 228 ticks.
Speed of the Hardware, Of Course
Obviously, the speed of the machine your application is running on affects the speed of _CopyBits. To make _CopyBits as fast as possible, spend a lot of money. However, there is more to the speed of _CopyBits than the speed of the Macintosh itself. When the Macintosh 128K was released, there was only one place for pixel images: main memory. Today, the situation is more complicated. If you have a modular Macintosh, the pixel image for the screen is in the memory of a NuBus™ video card. If you have a Macintosh IIci, you can optionally abandon the NuBus video card and use on-board video which takes up part of main memory. If you have an 8•24 GC card with enough memory, the pixel images can be cached in the card’s memory along with the screen’s pixel image.
All of these different locations have different access speeds, and that can affect the speed of _CopyBits. Additionally, different Macintoshes have different RAM access speeds. The Macintosh II, IIx, IIcx, and SE/30 have faster RAM than the Macintosh Plus or SE. The Macintosh IIci RAM access speed is faster still, and the Macintosh IIfx has faster RAM access than the IIci. Different video cards have different access speeds. The IIci has a cache card option which can vastly speed up on-board video RAM access speed. Third-party video cards that work in the Processor Direct Slot of the Macintosh SE and SE/30 have their own speed characteristics as well.
There can also be a speed cost for crossing the different areas. If _CopyBits copies between main memory and a NuBus video card, the image data has to be transferred across NuBus. NuBus is a speed bottleneck, so copying an image across NuBus is slower than copying the image from one part of the screen to another or copying from one part of main memory to another. Modifying the standard test to create two windows and two off-screen pixel maps—all eight bits deep with the standard color table then doing every combination of copying between off-screens, between windows, and between off-screens and windows produces the following results:
Off-screen to off-screen: 147
Screen to screen: 188
Off-screen to screen: 204
Screen to off-screen: 201
Performing the standard test on a Macintosh IIfx running System Software 6.0.5 with an Apple Extended Video Card yields a result of 153 ticks, which is not too shabby considering that the transfer is still going through NuBus.
Depth of Pixel Maps
This factor is pretty obvious and is sort of similar to the effect of the dimensions of the copied area: the more bits per pixel there are in the pixel map to copy, the more memory that _CopyBits has to move and the longer it takes to get the job done, assuming that the source and destination pixel maps have the same depth. To make _CopyBits as fast as possible, make sure the pixel maps are as shallow as possible.
If _CopyBits has to copy to a pixel map that has a different depth from the source pixel map, the relationship between speed and depth becomes more complicated. There is a tradeoff between the time taken to change the depth of an image and the absolute amount of data that has to be processed. Copying from a 1-bit deep pixel map to a 32-bit deep pixel map is not that slow because the amount of image data in the 1-bit deep pixel map is so small.
Modifying the standard test to transfer a four-bit deep pixel map to another four-bit deep pixel map produces a result of 78 ticks.
Color Mapping
Color QuickDraw expects a color table attached to every indexed pixel map. Color tables specify what color each pixel value in the pixel map represents. When an application calls _CopyBits to copy a pixel map into another pixel map, _CopyBits reproduces the colors of the image in the source pixel map as closely as possible—even if the colors available in the destination pixel map are different than those available in the source pixel map. This reproduction is done through a process called “color mapping.”
When color mapping is done, the source pixel values are transformed into RGBColor records using the source pixel map’s color table. These RGBColor records are passed to _Color2Index which finds the pixel values of the closest available colors in the current GDevice pixel map’s color table. This same process is done when the source and destination pixel maps have differing depths. The color table attached to the destination pixel map is not used in color mapping. The colors available in the current GDevice pixel map’s color table are used instead. So, the destination pixel map must have the same colors for the same pixel values as the current GDevice. Otherwise, the resulting image in the destination pixel map gets the wrong colors. See Inside Macintosh, Volume V-141, The Color Manager, for a description of _Color2Index. It’s also helpful to read the “Inverse Tables” section in the same chapter on page V-137.
Now, if the source color table contains virtually the same colors for the same pixel values as the current GDevice pixel map’s color table, then any particular pixel value has the same color regardless of whether it is in the source or destination pixel map. In this case, color mapping is a waste of time, because the pixels can be copied directly from the source pixel map to the destination pixel map without a loss of color fidelity. _CopyBits takes advantage of this special case to yield some big speed improvements. How is this special case detected? Before this question is answered, it’s useful to understand how Color QuickDraw uses color tables.
The ctSeed Field
The first field in a color table is the ctSeed field. This LongInt can be thought of as the color table’s version of the scrapCount field of the desk scrap. Whenever an application calls _ZeroScrap, the desk scrap’s scrapCount is changed. An application can tell that the desk scrap has changed by checking to see if the scrapCount has changed. Similarly, whenever the contents of a color table are changed in any way, the ctSeed field should be changed to indicate to anyone using that color table that it has been modified.
Additionally, Color QuickDraw often uses the ctSeed as a fast check for color table equality. If two color tables have the same ctSeed, then Color QuickDraw often assumes that their contents are equivalent.
After creating a new color table, an application has to get a valid value for the ctSeed field, and it can do so with the _GetCTSeed routine. This routine generates a valid ctSeed value suitable for a new color table. See Inside Macintosh, Volume V-143, The Color Manager, for a description of _GetCTSeed.
System Software 7.0 and 32-Bit QuickDraw each offer a routine called _CTabChanged which should be called after a color table is modified. It takes a handle to the changed color table as a parameter. If the _CTabChanged routine is not available, then the application should instead change ctSeed to a different valid value by calling _GetCTSeed and assigning the result to ctSeed, just like it’s done when the application creates a new color table. You must use either one of these methods to tell Color QuickDraw that the color table has changed, or else the modified color table could be confused with the old color table, or with some other color table—this is especially critical if an 8•24 GC card is being used. See the 32-Bit QuickDraw Developers’ Notes for details about the _CTabChanged routine.
The ctFlags Field
The ctFlags field is used as a set of flags that indicate some characteristics of the color table. Currently, only the top two bits of ctFlags are of any interest to developers. The most significant bit of ctFlags (bit 15) indicates whether the color table is a sequential color table or an indexed color table. Bit 14 indicates that the color table is a special kind of sequential table if it is set. In these kinds of color tables, the value fields indicate a palette entry in the destination window’s palette. See the Palette Manager section of the 32-Bit QuickDraw Developers’ Notes for a discussion about this capability.
Sequential Color Tables
If bit 15 of ctFlags is set, the color table is a sequential color table. Sequential color tables are usually found attached to GDevice pixel maps and to GWorld pixel maps.
In sequential color tables, the position of each color in the color table indicates the pixel value to which it corresponds. For example, the fifth entry in a sequential color table always has a pixel value of four (pixel values start at zero). The value field of each ColorSpec is not defined in sequential color tables, though they are used in color tables for screen GDevice records to indicate that a particular color is reserved, protected, or both.
Indexed Color Tables
If bit 15 and 14 of ctFlags are clear, the color table is an indexed color table. In indexed color tables, the value field of each ColorSpec indicates the pixel value of the RGB in that ColorSpec. For example, if the fifth ColorSpec in the color table has a value field containing 10, then that color has a pixel value of 10, not 4, as it would have been if this were a sequential color table.
Color Mapping or Non-Color Mapping
As noted before, _CopyBits can detect whether it has to do color mapping or not, so that it can take advantage of the speed benefits of no color mapping if possible. How is this done? First, _CopyBits checks to see if the ctSeed field of the source and destination color tables are the same and if the source and destination pixel maps have the same depths. If both of these conditions are true, then _CopyBits assumes that the two color tables are identical and it just copies the pixels directly without color mapping. If the ctSeed fields are different, _CopyBits checks manually through all of the colors in the source pixel map’s color table map to see if they map to the same pixel values in the current GDevice pixel map’s color table as they do in their own color table. If they do, then _CopyBits again takes the fast case.
So to keep _CopyBits as fast as possible, make sure that the source and destination color tables have virtually the same colors for the same pixel values. This applies even if one color table is an indexed color table and the other is a sequential color table, or if the source and destination color tables are both indexed but the order of the ColorSpec records differ.
Modifying the standard test so that the source pixel map has a color table that is the reverse of the standard eight-bit system color table (the grays have low pixel values and the light pinks and yellows have high pixel values) and the destination pixel map has the standard eight-bit system color table produces a result of 470 ticks.
By the way, color tables do not make any sense for direct pixel maps, so this discussion does not apply to them. Direct pixel maps do have a color table attached to them, but they’re just there so that an application that assumes that a color table is attached does not bomb.
Scaling
If the source and destination rectangles are the same size, _CopyBits has the fairly easy task of just transferring the pixels from the source pixel map to the destination pixel map; however, if the source and destination rectangles are different sizes, _CopyBits has to scale the copied image, which slows it down a lot. To keep _CopyBits as fast as possible, make sure the source and destination rectangles have the exact same dimensions.
Modifying the standard test to copy a 128 by 128 pixel portion of the source pixel map to the whole 256 by 256 pixel window produces a result of 1,159 ticks.
Of Time and Space
Hopefully, this Note makes it a lot clearer to you how to set up a situation in which your _CopyBits calls are as fast as your situation allows. It’s important to realize that this Note does not cover every single factor that has an influence on the speed of _CopyBits. There are many more factors which are just too unpredictable. For example, _CopyBits is highly optimized for many special cases, and those optimizations can have a big effect on the speed of the copy. Also, the speed of _CopyBits can be affected by interrupt-level tasks. It’s up to you to fine tune your programs to your particular situations.
Further Reference:
• Inside Macintosh, Volume I, QuickDraw
• Inside Macintosh, Volume V, The Color Manager
• Inside Macintosh, Volume VI, Color QuickDraw
• Technical Note M.IM.ColorCopyBits, Adding Color With _CopyBits
• d e v e l o p, January 1990, “Realistic Color for Real-World Applications”
• d e v e l o p, January 1990, “Braving Offscreen GWorlds”
• 32-Bit QuickDraw Developers’ Notes (APDA)
NuBus is a trademark of Texas Instruments
QD 505 - Basic QuickDraw Q&As
QuickDraw
Revised by: Developer Support Center September 1993
Written by: Developer Support Center October 1990
This Technical Note contains a collection of Q&As relating to a specific topic—questions you’ve sent the Developer Support Center (DSC) along with answers from the DSC engineers. While DSC engineers have checked the Q&A content for accuracy, the Q&A Technical Notes don’t have the editing and organization of other Technical Notes. The Q&A function is to get new technical information and updates to you quickly, saving the polish for when the information migrates into reference manuals.
Q&As are now included with Technical Notes to make access to technical updates easier for you. If you have comments or suggestions about Q&A content or distribution, please let us know by sending an AppleLink to DEVFEEDBACK. Apple Partners may send technical questions about Q&A content to DEVSUPPORT for resolution.
New Q&As for this month:
Determining whether CopyBits to a PICT was successful
Random function requires prior InitGraf call
GetIconCacheData & SetIconCacheData bug and workaround
DrawText and DrawString patched to be script aware
Determining whether CopyBits to a PICT was successful
Date Written: 1/7/93
Last reviewed: 3/1/93
If I’m recording a PICT and doing a CopyBits of a really big image into the PICT, how can I determine whether I’m out of memory?
___
The only reliable way to see whether a CopyBits to a PICT succeeded is after the fact. You need to test the PICT’s picFrame rect (Inside Macintosh Volume V, page V-87) to see whether it’s empty after the CopyBits. The test would look like:
If (EmptyRect(&(**myPicture).picFrame))
/* CopyBits failed */
Random function requires prior InitGraf call
Date Written: 12/16/92
Last reviewed: 3/1/93
The Random function call listed in QuickDraw.h can’t be called from MPW tools without crashing my system. It appears to work when the function is called from applications or cdevs. What could be causing this problem?
___
Use SANE’s RandomX function instead of QuickDraw’s Random function if possible because it gives you better randomness. If you do use QuickDraw’s Random function, be sure to call InitGraf before calling Random from any application or tool. InitGraf initializes a set of QuickDraw global variables for use with the QuickDraw tools; these globals must be initialized because the Random function uses one of them as a seed to generate the random number.
Normally, it’s not good practice to call initialization routines from within an MPW tool, but calling InitGraf is OK. For more information on which initialization routines are OK to call and which ones aren’t, see page 7 in the MPW Tools chapter of Building and Managing Programs in MPW.
Sometimes it isn’t obvious when you need to call InitGraf before using the Random function. For example, if you’re using the Macintosh serial tool in a faceless background application, you’ll need to initialize QuickDraw because the tool calls Random.
GetIconCacheData & SetIconCacheData bug and workaround
Date Written: 12/8/92
Last reviewed: 3/1/93
The Icon Utilities routine GetIconCacheData leaves two bytes of garbage on the stack. This surfaced as a problem for me because it prevented a saved register from getting restored properly. SetIconCacheData probably has the same problem, since it calls the same trap internally. I solved the problem by encapsulating GetIconCacheData within my own static function, like so:
I then call GetIconCacheData normally. The #define redirects my call to my static wrapper function. The extra two bytes on the stack are recovered when the wrapper function UNLKs and returns. This method has the advantage that the calling code will still work even after the trap is fixed. Am I correct?
___
You’re quite correct; this is a bug in GetIconCacheData and SetIconCacheData. Here’s the offending code from the source:
EXIT MOVEA.L (SP)+, A0 ; Pop return address into A0
ADDQ.L #6, SP ; Point stack at return value
MOVE.W D0, (SP) ; Put return value on the stack
JMP (A0) ; Return
As you can see, the exit routine is adding 6 to the stack to clear up the input parameters instead of 8 (handle and handle), so an extra word of garbage is being left on the stack. Thanks for letting us know about the problem.
DrawText and DrawString patched to be script aware
Date Written: 11/16/92
Last reviewed: 6/14/93
While localizing our software, we were told not to assume that a character is only one byte, and thus not to use DrawChar. Does this mean that we can’t use DrawText or DrawString?
___
DrawChar takes a one-byte character as a parameter, so it isn’t suitable for drawing a character whose internal representation requires two bytes. However, DrawText and DrawString (both end up in the same bottleneck procedure StdText) are patched in script-aware systems, and do recognize whether a given byte in a given font-script still corresponds to a one-byte character, or is the first byte of a two-byte character. In the latter case, it transparently fetches the next byte, and looks up the right glyph encoded by a double byte, before actually drawing the glyph.
QuickDraw globals at INIT time
Date Written: 6/1/92
Last reviewed: 9/15/92
If I call InitGraf before I reference CurrentA5, will CurrentA5 be valid and can the QuickDraw globals be referenced off it? The screenBits bounds values seem screwy on some machines. Does the problem lie with CurrentA5? Should I be referencing A5?
___
Here’s the process used by ShowINIT, which is remarkably compatible with system software and other INITs (and it had better be, because it’s used by more than half the system extensions available):
1. It saves the value in the CurrentA5 global to restore it later.
2. It points the A5 register at 4 bytes of storage for use by the system.
3. It copies the value now in A5 into the CurrentA5 global.
4. It calls InitGraf, passing a pointer to the thePort field of a QuickDraw globals structure.
5. It opens a port and draws as necessary. [This is where all the functionality goes.]
6. After it’s done, it closes its port.
7. It copies the value saved in step (1) into the A5 register.
8. It copies the restored A5 value into the CurrentA5 global.
To summarize, ShowINIT saves the A5, creates and initializes its own A5 world, does its drawing, then restores the previous A5 world. For more information on this subject, see the Macintosh Technical Note “Stand-Alone Code.”
Macintosh QuickDraw LineTo bug and workaround
Date Written: 4/23/92
Last reviewed: 7/13/92
Our zooming function crashes into flames when we pass valid coordinate values to LineTo, as in the following example:
SetPort(myPort);
MoveTo(154,31619);
LineTo(74, -31742); (* You are dead! *)
What can we do to avoid LineTo crashes like this?
___
The QuickDraw Engineering group is aware of the problem you described. The bug probably is going to be fixed in the next release that includes bug fixes. Given that waiting for a system solution may demand more patience than is reasonable, you may want to consider including in your software some form of workaround that will prevent your users from crashing every time an operation takes the software to the limits of QuickDraw.
One way to approach this problem is to replace the lineProc bottleneck. All you need to do is to check the distance between the current pen position and the line’s end, and when the distance becomes too big (let’s say more than 32000) your procedure will call StdLine a couple of times, splitting the operation in two.
Replacing the bottlenecks is a very straightforward operation (which you are probably already using) and in most of the cases will only result in another level of indirection into StdLine but that will prevent your program from calling QuickDraw with parameters that are guaranteed to cause crashes.
Use crsrNew flag to unobscure cursor without mouse move
Date Written: 3/3/92
Last reviewed: 6/14/93
The Macintosh QuickDraw routine ObscureCursor hides the cursor until the next time the mouse is moved, but it isn’t affected by HideCursor or ShowCursor. Our application needs to use ObscureCursor while the user is typing but needs the cursor to be visible after no typing has occurred for a short period. How do we “undo” ObscureCursor, since we can’t rely on the user to move the mouse?
___
The only way (besides actual mouse movement) to make an obscured cursor visible again is to convince the system that the mouse has moved. There’s no really good way to do this via Toolbox calls, so you’re going to have to do it the hard way and simply update the low-memory cursor information to tell the system the cursor moved (even though you don’t need to update the actual position).
To tell the system the cursor has changed location, simply set the crsrNew flag (a byte located at $08CE) to 1. When the system sees this byte is 1, it will assume the cursor has moved and redraw the unobscured cursor at the appropriate place (where it was all along), and reset CrsrNew, waiting for the mouse to move again.
Macintosh CalcMask and CopyMask code sample
Date Written: 2/27/92
Last reviewed: 6/14/93
I can’t get the black-and-white version of my lasso-type tool to work correctly with CalcMask and CopyMask. With CalcCMask it seems to work fine. What could I be doing wrong?
___
CalcMask and CalcCMask are similar in that they both generate a 1-bit mask given a source bitmap. With CalcCMask, though, a pixMap can be used in place of the source bitmap; the seedRGB determines which color sets the bits in the mask image. An easy mistake to make is to forget that CalcCMask accepts a pointer to a bitmap data structure while CalcMask expects a pointer to the actual bit image. And unlike CalcCMask, which uses bounding Rects for the image’s dimensions, CalcMask uses the bitmap’s rowBytes and pixel image offsets to determine the bounding Rects for the image. A typical call to these routines would be
One last thing to note when using CalcMask is that the width of the image is in words and not bytes. To learn more about these routines, see page 24 of Inside Macintosh Volume IV and page 72 of Inside Macintosh Volume V. Also, the Developer CD Series disc contains a sample, CalcCMask&CalcMask, that shows how to use both these routines.
Code for filling an area fully bounded by polygon
Date Written: 2/21/92
Last reviewed: 6/14/93
Currently, when a polygon is filled, an even-odd rule is applied to determine which areas of the polygon are to be filled. For our application, we also need to fill all the areas of the defined polygon. Is there a relatively easy way to accomplish this?
___
There are many different ways to fill polygons, as you may know. If you do not want to use QuickDraw’s standard FillPoly routine, you’ll have to create your own. The following sample illustrates one technique that might be used to fill the area fully bounded by a polygon. It can be dropped right into the traffic light sample (sample.p) that ships with MPW as a replacement for its DrawWindow procedure. The green star is drawn using FillPoly and the black star is drawn using my filling technique that uses an offscreen bitmap and calcMask to fill in the poly the desired way, then CopyBits to transfer it to the onscreen port. The drawbacks of this method are that it is not as fast as writing a specialized poly routine; the benefits are that it’s small, fast enough for most operations, and can be used for more than just polygons.
{$S Main}
PROCEDURE DrawWindow(window: WindowPtr);
var MyPoly:PolyHandle;
MyRgn :RgnHandle;
OffPort,OnPort:GrafPtr;
Function CreateOffport(VAR newOffscreen:grafPtr;
inBounds:Rect):Boolean;
var SavePort,NewPort:Grafptr;
begin
GetPort(SavePort);
NewPort:=GrafPtr(NewPtr(sizeof(grafport)));
If MemError<>noErr then Begin
CreateOffport:=false;
EXIT(CreateOffport);
END;
OpenPort(newPort);
With newPort^ do begin
portRect :=Inbounds;
RectRgn(ClipRgn,inBounds);
RectRgn(visRgn, inBounds);
End;
With newPort^.PortBits DO BEGIN
Bounds:=Inbounds;
rowBytes:= ((inBounds.right-inBounds.Left+15) DIV 16) *2;
baseAddr:= NewPtr(rowBytes
* LONGINT(inBounds.Bottom-inBounds.Top));
End;
If MemError <>noErr THEN BEGIN
SetPort(SavePort);
ClosePort(newPort);
DisposPtr(ptr(newPort));
CreateOffport:=false;
END
ELSE BEGIN
EraseRect(inBounds);
newOffscreen :=newPort;
setPort(SavePort);
CreateOffPort:=true;
end;
end;
Procedure KillOffPort(oldOffscreen :GrafPtr);
Begin
ClosePort(oldOffscreen);
DisposPtr(OldOffscreen^.portBits.baseAddr);
DisposPtr(ptr(OldOffScreen));
End;
BEGIN
If NOT (CreateOffPort(offPort,window^.portRect)) THEN Exit(DrawWindow);
If NOT (CreateOffPort(onPort,window^.portRect)) THEN Exit(DrawWindow);
Inside Macintosh Vol. V PICT opcode size should be fixed
Date Written: 1/22/92
Last reviewed: 2/28/92
The definition of PICT version 2 on pages 92-105 of Inside Macintosh Volume V says that the data size of the opcodes $001A and $001B is variable, but also that the data is an RGBColor. This is confusing, since the size of an RGBColor is fixed at six bytes. How can these two opcodes vary in the amount of associated data?
___
Seems like you’ve run into a cut/paste problem. All the opcodes that refer to table 4 are new for Color QuickDraw. Also, most of them are variable in length, so the author simply had a standard notation for anything that was explained further in table 4 (page V-103). The information contained in table 4 is, in fact, accurate. The size information of several of the opcodes listed is not variable even though the preceding pages told you they were.
All you gotta do is believe table 4 and you will be fine.
PICTs with PostScript PICT comments and memory use
Date Written: 1/10/92
Last reviewed: 6/14/93
Why does my PICT (including dotted lines) use so much memory when drawn in MacDraw, and even more when drawn in SuperPaint? Do they include PicComments for PostScript?
___
Your guess that it has to do with PicComments is quite right; both MacDraw and SuperPaint include a PostScript representation of the dotted (dashed) lines and some other graphic operations in the PICT, together with the QuickDraw commands. During printing, this allows the LaserWriter driver to take advantage of specific PostScript capabilities that are unavailable in QuickDraw, like primitives for dashed lines.
On the other hand, the PostScript representation for dashed lines is much shorter than the QuickDraw representation, which requires a (long, very long …) sequence of “ShortLine” opcodes. So, another piece of explanation for the large PICT size basically is that QuickDraw does not have facilities to describe dotted lines in an economic way.
SuperPaint also includes a copy of a proprietary dictionary, which adds substantially to the size of a PICT. On the other hand, the code that resides in that dictionary makes the picture’s PostScript representation that much better. Ultimately, WYSIWYG is the goal, and sometimes it takes a little extra code to make that happen. (Incidentally, the PostScript dictionary contained in pictures created by older versions of SuperPaint makes assumptions about the contents of the LaserPrep file which are not true for the recent versions of the LaserWriter driver. Documents containing such pictures will not print correctly any more.)
To determine the primitives that define other nonstandard QuickDraw objects found in drawing applications, you can use MPW’s DeRez function or a third-party utility such as Palomar Software’s PICT Detective on the resource PICT. These tools will provide the opcodes that define the PICT.
Where CopyBits looks for memory to use
Date Written: 1/3/92
Last reviewed: 1/27/92
Where does CopyBits look for the memory it needs?
___
CopyBits checks the stack to determine if there is enough stack space for it to copy the whole image, which in some cases may be roughly up to 5 extra rowbytes of special effects per row, depending on what special effects such as dithering or scaling are being used. If there is not enough stack space for the whole image, CopyBits then tries for half the image, and keeps halving until it gets down to one row of the image (plus the room for the special effects rows). If there is not enough stack space for one row of the image, then CopyBits tries to allocate temporary memory.
Before allocating temporary memory, CopyBits checks if the temporary memory traps are available. (They are available under both System 6 MultiFinder and System 7.) If the traps are available, CopyBits tries to allocate a 256K byte buffer for use as a “fake” stack. (CopyBits used to try for a 64K block, but this has been changed, and it may change again.) If this succeeds, then all is well and the image is copied. If the temporary memory traps do not exist, or if CopyBits cannot allocate a 256K buffer, then the image is not copied and CopyBits returns.
CopyBits does not check in the application heap for free memory, at least not for its work buffer. For its work buffer it will only use the stack, and after that it resorts to temporary memory, if available. There are some circumstances that may cause memory allocations in the application heap, but this memory is not used for CopyBits’s image buffer.
Also, please note that the implementation of CopyBits is subject to change in future versions of QuickDraw.
GrafPort patStretch: valid values
Date Written: 12/19/91
Last reviewed: 6/14/93
I’d like to know more about that PatStretch field inside a GrafPort or CGrafPort. If I stuff a values in PatStretch(4) then nothing happens; prints look the same, even using a standard bottleneck. Please tell me how I can get this to work.
___
PatStretch only works with values of 2 or 3. With any other value, it defaults to no stretching. The “2” case was created because of the ImageWriter (72->144 dpi) situation. The “3” case was added to support the ImageWriter LQ and the AppleFax modem.
So why wasn’t a “4” (72->300 dpi) handler added for the LaserWriter driver? Good question. Somehow or other it was decided that pattern stretching for the LaserWriter driver would be done completely by the driver itself. The LaserWriter driver actually does pattern stretching by using a pattern 4 times as large, rather than 4.17. In other words, it really scales the 72 dpi pattern to 288 dpi rather than 300 dpi. You may want to take a similar approach, since you’d only have to work with whole numbers this way.
So, if you want to do 4-times pattern stretching, you must scale the pattern yourself. If you copy the original pattern into an area that’s twice as wide and twice as tall and use that, you should be all set. You’ll need to use PrGeneral to set the printer to the appropriate resolution and Copybits to copy the pattern into the object that needs to be filled, using the “cookie cutter” approach to fill the object.
X-Ref:
Inside Macintosh Volume I, page I-150
How to tell whether GetPictInfo is available
Date Written: 12/16/91
Last reviewed: 6/14/93
How do you determine whether the Picture Utilities Package function GetPictInfo is available? Gestalt doesn’t seem to have the right stuff!
___
To determine whether the GetPictInfo routine is available, check the system version number with the Gestalt function. GetPictInfo is available in system software version 7.0 and later. Use the Gestalt selector gestaltSystemVersion to determine the version of the system currently running. Usually it’s best not to rely on the system version to determine whether features are available, but in this case, it’s the only way to determine whether the Picture Utilities Package is available.
For example, the following C function will determine whether the GetPictInfo call is available:
#include <GestaltEQU.h>
Boolean IsGetPictInfoAvail()
{
OSErr err;
long feature;
err = Gestalt(gestaltSystemVersion,&feature);
/* Check for System 7 and later */
return (feature >= 0x00000700);
}
In Inside Macintosh Volume VI, see page 3-42 for information on using Gestalt to check the system version number, and see page 18-3 for information on the Picture Utilities Package.
Detecting whether application window is partially hidden
Date Written: 9/26/92
Last reviewed: 6/14/93
We draw directly to the screen to gain the fastest possible animation speed, and when we need compatibility—such as when windows overlap or for multiple screens—we do use CopyBits. How do we tell whether the window is hidden or that the visible part is not rectangular?
___
If your window is covered partially by another applications window or if your layer has been hidden by the process menu, the visRgn of your window’s grafport will not be the portRect anymore. (Keep in mind that if you scroll by modifying the portRect of the grafport, then you’ll have to do a more complex calculation...) Here is a small Pascal routine that returns this information:
Function UseCopyBits(thePort:grafptr):Boolean;
begin
UseCopyBits:= NOT( (thePort^.VisRgn^^.rgnSize=10) and
(thePort^.visRgn^^.RgnBBox=thePort^.PortRect) );
end;
The rect strucRgn^^.rgnBBox will be zero for a visible window if the system
has hidden the application.
CopyBits bug and workaround
Date Written: 6/26/91
Last reviewed: 6/14/93
Has anyone run across what I’m told is a bug in CopyBits? It works like this: In the deep, dark workings of CopyBits, some routine tries to read the two bytes preceding the baseAddress of the source PixMap. If the baseAddress is at the start of a card’s NuBus space and there isn’t a card filling the adjacent space, this causes a bus error! Has anyone found a good workaround?
___
The short answer is: you’re right. QuickDraw inadvertently reads from memory below the base address of a pixmap. The workaround is to place the video base address 32 bytes into the slot memory space for the card; if the card you’re using doesn’t have this workaround, there’s nothing you can do other than making sure there’s a card in the next-lower slot.
Macintosh animation samples
Date Written: 11/6/91
Last reviewed: 6/14/93
Do you have an example of flicker-free animation on the Macintosh?
___
We have some good stuff that’s written in MPW Pascal. It’s DTS Sample Code #16, OffSample, and this uses some routines defined in DTS Sample Code #15, OffScreen. Also, the System 7.0 CD sample code folder contains a smaller sample called “GMonde” that uses GWorlds.
System 7 QuickDraw DrawText performance
Date Written: 11/4/91
Last reviewed: 11/27/91
We’ve noticed that using DrawText is much slower in System 7, especially when drawing in color (anything other than black on white). What can be done to restore the drawing speed to System 6 levels?
___
A QuickDraw function like DrawString or DrawText will be slower under certain circumstances in System 7 than System 6. Specifically, if you are drawing in srcCopy mode and you colorize the text—that is, foreground color is not black and background color is not white (Inside Macintosh Volume VI, page 17-16)—then QuickDraw really slows down as you have noticed. Sometimes, the speed of drawing is 6 times as slow as System 6.
The cause of this slowness is a known System 7 bug. The bug has concerned the engineers greatly and will be responded to in an appropriate manner in the future.
There are a few workarounds: One, you can avoid using the srcCopy mode and use the default srcOr mode instead. However, this is not a real workaround, since you may have essential reasons to use srcCopy. The other option is to create an offscreen pixmap or GWorld and perform a DrawText with srcOr to this GWorld with colorization. Then, you can perform a CopyBits from the offscreen to the screen with srcCopy mode and no colorization. Using CopyBits will not cost you much time. Again, this is a workaround and is not ideal.
The srcOr is a bit slower than in System 6.0.x, but it does not have a bug; rather it is a side effect of system enhancements. The slow speed is a trade-off taken to receive the host of other benefits.
Updating Macintosh cursor without mouse competition
Date Written: 6/12/91
Last reviewed: 6/14/93
How can I programmatically move the Macintosh mouse without the real mouse interfering?
___
The real answer to your question is twofold: First, you can do exactly what you want to do with the sample included below. However, this is not a good thing to do, it would be better if you took the solution used in Apple’s Guided Tour disks: Always hide the cursor and then decouple the cursor from the mouse. Then, instead of using the system’s cursor, simply draw your own “cursor” using QuickDraw and treat it as a little animated bitmap on the screen. This avoids all the problems that you have with the mouse competing. (Apple does update the mouse globals with the mouse position so that other things function correctly.)
Now, as promised, here is the way to do what you want using the real cursor. As you have discovered, setting the crsrCouple variable to false prohibits the mouse from affecting the cursor; unfortunately, it also prohibits the jcrsrTask routine from drawing the cursor. The solution to this is to set crsr couple to true, call the cursor drawing routine jCrsrTask yourself, and then set the crsrCouple variable to false, as shown below:
procedure callcrsr;
inline $2078 ,$08EE ,$4E90;
{ move.L jcrsrTask,A0
jsr (A0) }
Procedure FudgeMouse;
type PointPtr=^Point;
var RawMouse:PointPtr;
MTemp:PointPtr;
RandPt:Point;
CrsrNew:ptr;
CrsrCouple:ptr;
fred:Longint;
begin
RawMouse:=PointPtr($82C);
MTemp:=PointPtr($828);
CrsrNew:=ptr($8CE);
CrsrCouple:=ptr($8CF);
RandPt:=RawMouse^;
repeat
RandPt.h:=RandPt.h+1;
RandPt.V:=RandPt.v+1;
RawMouse^:=RandPt;
MTemp^:=RandPt;
CrsrNew^:=1;
CrsrCouple^:=1;
callCrsr;
crsrCouple^:=0;
repeat until fred<tickCount;
fred:=tickCount+3;
until Button;
crsrCouple^:=1;
end;
Techniques for graying Macintosh text
Date Written: 6/3/91
Last reviewed: 6/14/93
How do I draw grayed-out text on the Macintosh, like the text for disabled buttons or menu items?
___
There are currently two different kinds of grayed text: First, there’s “patterned” gray, where every other dot is missing. This really only looks good with Chicago or other heavy fonts and was always used for graying out menus and controls in system software through 6.0.x, and is still used in 7.0 when the screen is set to less than 4 bits deep. This is done by first drawing the text in a normal, srcCopy transfer mode. Then a gray rectangle is drawn over the text using the patBic mode. This “erases” half the bits in the text, and is rapid enough that there is very rarely any flicker.
The second kind of text is the actually gray text, which is used in System 7 on screens that are 4 bits deep or deeper for menus, controls, and other grayed text. To draw this text, just call GetGray (as documented on page 17-27 of Inside Macintosh Volume VI) to get an appropriate gray. Then draw the text in that color.
Use srcOr instead of srcCopy for Macintosh text drawing
Date Written: 6/4/91
Last reviewed: 10/9/91
DrawText with srcCopy takes six times as long as with srcOr now that my Macintosh is running System 7. Why is this so slow? Is this a bug in System 7?
___
It’s true that srcCopy is slower than srcOr when handling text, especially in color mode. This loss in speed occurs because CopyBits is a lot smarter than it used to be. It can handle foreground and background colors a lot better, but that improvement came at the cost of speed. Our recommended method for drawing text is to erase before drawing, and use srcOr to draw, not srcCopy. Alternatively, you could draw colorized text in srcOr mode off screen and then use CopyBits to draw it on the screen in srcCopy mode without colorization.
Code for reversing Macintosh PICT images
Date Written: 3/4/91
Last reviewed: 6/14/93
Is there a simple way to put PICT images up in mirror image format, or is there sample code showing how to flip an offscreen bitmap?
___
There is no easy way to do this, nor do we have sample code showing how to flip an offscreen bitmap. Indeed, the best way to do what you want is to draw it to an offscreen pixel map and reverse it.
If you are using Color QuickDraw, always draw it to an 8-bit-per-pixel offscreen bitmap, and then the reverse is a very simple task. Here is some sample Pascal code that might roughly do what you want, with the following assumptions:
1. You are going to add error checking where appropriate.
2. Rowbytes correspond exactly to pixel width of the port.
{ The scan line is simply an array [0..RowBytes] of Byte, and since this is }
{ 8 bits per pixel, each one is a single pixel.}
Size:=thePixMap^.RowBytes;
For Index:=0 to (Size div 2) do
begin
tempPixel:=ThisScanLine^[Index];
ThisScanLine^[Index]:=ThisScanLine[Size-Index-1];
ThisScanLine^[Index]:=tempPixel;
end;
end;
This same procedure can be used also to swap a 1-, 2- or 4-bit-per-pixel pixmap if you add a function that accepts a byte and swaps the pixels in it.
Using dithered drawing mode with QuickDraw
Date Written: 11/28/90
Last reviewed: 12/19/90
When I draw a 32-bit Macintosh PICT image from a file to an 8-bit port via an offscreen GWorld, I use dither mode in the CopyBits call and the results are quite impressive. If there is not enough memory to allocate the GWorld, I draw the image directly to the port. But since there does not seem to be any way to tell QuickDraw to use dithered drawing mode, the image looks horrible.
Do you have any suggestions? I have installed bottleneck procs to allow DrawPicture to get its data from the file instead of the handle in memory. Is there a way, while in the bottlenecks, to find the CopyBits call that comes from the picture and force it to use dithered mode instead of source mode? I don’t want to try and parse the PICT myself, but I thought that maybe a QuickDraw global could be modified in my StdBits proc to force dithered drawing for that operation only?
___
You can install a StdBits or bitsProc bottleneck procedure to get all the CopyBits calls when the picture is being played back. One of the parameters to the StdBits call is the mode. You can install a procedure that saves the current mode, and then passes ditherMode to the original StdBits proc. This is all you should need to do. It’s been done here so we know it works, only not in any form that can be sent to you as sample code at this time.
Using PicComments to rotate text
Date Written: 11/28/90
Last reviewed: 12/19/90
I have a PostScript routine (using TextBegin/TextEnd) to generate bitmapped rotated text on the screen (which can be later printed on QuickDraw printers). Why do I get duplicate text? I get both bitmapped rotated text and PostScript rotated text when I print on the LaserWriter II, and both bitmapped rotated text and horizontal text on the ImageWriter. When I make a machine dependent check (check type of printer) and call the proper printing procedure, it works fine. Because of the speed and memory considerations of generating the rotated bitmapped text (especially at 300 dpi), is there a way to ensure that the printer will use the PostScript BEFORE generating the bitmap?
___
We will use the following Macintosh PicComments to hide your QuickDraw calls from the LaserWriter, but the ImageWriter will use them:
PostScriptBegin
>> Put your CopyBits and QuickDraw calls to image your rotated
>> bitmapped text here....
PostScriptEnd
By wrapping your QuickDraw code within the PostScriptBegin and PostScriptEnd PicComments, the code will be ignored by the LaserWriter, but the ImageWriter will use the QuickDraw calls. Basically, the PostScriptBegin and PostScriptEnd PicComments tell the LaserWriter driver to turn “off” QuickDraw. In the ImageWriter case, the ImageWriter does not understand the PicComments. Therefore, it will use the QuickDraw calls to create and image your bitmapped text.
Now, we need to use the rotation PicComments to rotate the text on the LaserWriter, but have the ImageWriter ignore the code:
Rect zeroRect;
SetRect (&zeroRect, 0, 0, 0, 0);
TextBegin
TextCenter
ClipRect (&zeroRect);
>> Draw your text to be rotated on the LaserWriter....
ClipRect (&rPageRect);
TextEnd
Wrapping your text drawing call(s) between the ClipRect calls will ensure that the text is drawn only on the LaserWriter. Setting the ClipRect to zero tells the ImageWriter to ignore all QuickDraw calls until the ClipRect is reset to something “real” (actually, a zero ClipRect prevents QuickDraw from drawing anything). After we have completed drawing the rotated text, we reset the ClipRect to the dimensions of rPage (that is, rPage is the image-able area of the currently selected printer—see Inside Macintosh Volume II, page 150). This will allow all of your normal drawing to continue on the ImageWriter and LaserWriter. If you did not reset the ClipRect after the TextEnd call, nothing would be drawn on the ImageWriter or LaserWriter.
Why grafPort’s clipRgn should be changed before OpenPicture
Date Written: 11/1/90
Last reviewed: 12/19/90
On page 189 of Inside Macintosh Volume I, in the QuickDraw chapter’s description of OpenPicture, is the following warning: “A grafPort's clipRgn is initialized to an arbitrarily large region. You should always change the clipRgn to a smaller region before calling OpenPicture, or no drawing may occur when you call DrawPicture.” The “arbitrarily large” clipping region rectangle is set to -32767,- 32767,32767,32767 (top, left, bottom, right) for new ports. This is the largest rectangle possible. If this is not a "valid" clipping rectangle for pictures, what is? Is there some specific limit to the size of the clipping rectangle? Does it depend on either available memory or the size of the picture?
___
Inside Macintosh’s warning is based on truth but it’s incomplete. It didn’t actually say that this rectangle is invalid as a clipping region, because this is in fact a perfectly valid clipping region. But, you could run into problems if you use this as a clipping region when creating a QuickDraw picture. It’s not a matter of available memory or size; it’s a simple matter of 16-bit signed integer overflow and underflow.
When you open a picture, the current clip region is recorded in the picture (this wasn’t necessarily true in some early versions of QuickDraw). When you draw the resulting picture using the picture’s picFrame as the destination rectangle, there won’t be any problems. But if you use a destination rectangle that’s larger than the picFrame, QuickDraw scales everything in the picture proportionately, including the clip region. If you allowed the default clip region to be recorded into the picture, then its rgnBBox, already as large as possible, will be made even larger. That means that the -32767 coordinates might wrap around to the positive number range, and the 32767 coordinates might wrap around to the negative number range. This leaves you with an empty clip region. Nothing at all gets drawn when the current port’s clip region is empty.
If the destination rectangle is smaller than the picture’s picFrame, you won’t have any problems because the default clip region will be made smaller, and that’s no problem.
This is why Inside Macintosh suggests that you make the clip region smaller than the default clip region before opening a picture. By doing this, you’re almost guaranteed that the clip region won’t get scaled to the point that it turns inside out. What size should you make it? Small enough so that the risk of the clip region’s coordinates being scaled out of QuickDraw coordinate space is minimal. I usually just set the clip region to the picFrame of the picture. It’s hard to go wrong this way.
Calling InitCursor instead of SetCursor
Date Written: 10/23/90
Last reviewed: 6/14/93
Is it legal to call InitCursor instead of SetCursor(arrow) when I want to set the cursor to an arrow (after my normal one-time program initialization code, in my UpdateCursor routine)? The only reason I'd want to do such a skanky thing is to save code. Calling a trap with no parameters is less code than one with parameters. What, exactly, if anything, does InitCursor do besides setting the cursor to an arrow and setting the cursor level to zero?
___
There's no problem at all with this, as long as you are aware that the hidden, busy, and obscured states are cleared when you call InitCursor, so if the cursor was hidden or obscured for good reason it'll suddenly reappear. It also gets the arrow from QuickDraw, of course, but that's not a problem.
Macintosh PICT-to-PostScript conversion
Date Written: 8/3/90
Last reviewed: 10/8/91
How do I convert PICT format data to PostScript in my printer driver?
___
Converting PICT files to PostScript involves a detailed understanding of both bitmaps (or pixmaps) and the graphics state in PostScript, which is a data structure defining the context in which other graphic operators in PostScript execute. If you don’t know PostScript, the following manuals are a must:
• PostScript Language Tutorial and Cookbook (Addison-Wesley) is an introduction to PostScript. • PostScript Language Reference Manual (Addison-Wesley).
• PostScript Language Program Design (Addison-Wesley) details designing efficient PostScript programs. It has a lot of useful sample programs on topics like writing a print spooler.
You need to convert all the QuickDraw operations in a PICT to corresponding PostScript operations. To get a feel for this conversion, you can analyze the PostScript dump from a LaserWriter to see how it converts a PICT to PostScript. Under System 6.x, a PostScript dump can be obtained by pressing Command-K while printing. Under System 7.0, you can get a dump by selecting the PostScript File option in the Print dialog.
Some areas of QuickDraw, such as transfer modes, do not have a correspondence in PostScript. The PostScript imaging model is designed so that all areas of a page affected by an image are marked as if with opaque paint. Using image masks can help. See the Graphics chapter in the PostScript reference manual.
PICT-to-PostScript conversion can be a long process, especially if one is unfamiliar with PostScript. Using the above books and the PostScript dump from the LaserWriter (but ONLY as a general guide) should help.
Sending PostScript via PostScriptHandle PicComment
Date Written: 5/1/90
Last reviewed: 10/9/91
If I use the PostScriptHandle PicComment to send PostScript code to the LaserWriter driver, do I need to open a picture and then draw the picture to the driver, or can I just use the PicComment with no picture open while drawing to the printer’s grafPort?
___
You don’t need to create a picture with your PicComment in it and draw the picture to the driver. The best method for sending PostScript code to the LaserWriter is to use the PostScriptHandle PicComment documented in the Macintosh Technical Note “Optimizing for the LaserWriter—Picture Comments,” as shown below.
PrOpenPage(...)
{ Send some QuickDraw so that the Printing Manager gets a }
The above code prints a line on any type of printer, PostScript or not. The first MoveTo/LineTo combination is required to give the LaserWriter driver a chance to define a clipping region. The LaserWriter driver replaces the grafProcs record in the grafPort returned from PrOpenDoc. In order for the LaserWriter driver to get execution time, you must execute a QuickDraw drawing routine that calls one of the grafProcs. In this case, the MoveTo/LineTo combination calls the StdLine grafProc. When StdLine executes, it notices that the grafPort has been reinitialized, and therefore initializes the clipping region for the port. Until the MoveTo/LineTo combination is executed, the clipping region for the port is set to (0,0,0,0). If PostScript code is sent via the PostScriptHandle PicComment before executing any QuickDraw routines, all PostScript operations will be clipped to (0,0,0,0).
The next thing that’s done is to send the PostScriptBegin PicComment. This comment is recognized only by PostScript printer drivers. When the driver receives this comment, it saves the current state of the PostScript device (by executing the PostScript gsave operator), then disables all QuickDraw drawing operations. This way, the QuickDraw representation of the graphic will be ignored by PostScript devices. In the above example, the second MoveTo/LineTo combination is executed only on non-PostScript devices.
The next PicComment is PostScriptHandle, which tells the driver that the data in thePSHandle is to be sent to the device as PostScript code. The driver then passes this code unchanged to the PostScript device for execution. The PostScriptHandle comment is recognized only by PostScript printer drivers.
The last PicComment, PostScriptEnd, tells the driver to restore the previous state of the device (via a PostScript grestore call), and to enable QuickDraw drawing operations.
Since most PicComments are ignored by QuickDraw devices, only the QuickDraw representation is printed. Since PostScriptBegin tells PostScript drivers to ignore QuickDraw operations, only the PostScript representation is printed on PostScript devices. This is a truly device-independent method for providing both PostScript and QuickDraw representations of a document.
Macintosh QuickDraw region quirks
Date Written: 1/1/90
Last reviewed: 11/21/90
I’m working with regions, and I’m having problems with Macintosh QuickDraw trashing the heap and crashing, even though my regions are under 32K.
___
There are some quirks in the current version of QuickDraw. Here are some the commonly-encountered problems:
1. When doing operations which use more than one region, sduch as UnionRgn, DiffRgn, XorRgn, or SectRgn, the sum of the sizes of the source regions must be less than 32K, regardless of the size of the resulting region.
2. FrameRgn will fail if it tries to frame a region bigger than 16K.
3. If CloseRgn fails, the internal region data is already corrupt; there is nothing you can do to recover. CloseRgn will also fail if there isn’t at least a 32K block of free space available.
Here are some workarounds:
1. Keep regions small and not too complex. Keep track of the sizes of all regions so you can check the SUM of the sizes before calling a routine that has a 32K limit.
2. Keep 32K free, or allocate a 32K block and release it just before calling CloseRgn.
Apple is working on these problems and expects to fix them in future versions of QuickDraw.
How to get Macintosh QuickDraw arc endpoints
Date Written: 1/1/90
Last reviewed: 6/14/93
Is there a way to obtain the endpoints of an arc drawn by the Macintosh QuickDraw arc routines, such as FrameArc and PaintArc?
___
Given a rectangle R which frames the arc you wish to draw, convert your angles to an absolute coordinate system, where three o’clock is 0 degrees and 12 o’clock is 90 degrees.
Now, let:
x = .5 (+ or -) (R.right - R.left)
y = .5 (+ or -) (R.bottom - R.top)
The endpoint of the curve will be defined by:
EndPoint.h = x (+ or -) cos(ang);
EndPoint.v = y (+ or -) sin(ang);
h & v are relative to center of rectangle R
This calculates only the upper endpoint of the arc, but you can easily calculate the other endpoint using the same formula by calculating the absolute angle for the start point and applying the same formula.
Here is a subroutine which illustrates the algorithm, in ThinkSpeed Pascal:
{ DrawCurve: draw an arc from 0 degrees until the point defined }
{ by 'angle'. At that point draw a 4 by 4 crosshair. }
x := (frame.right + frame.left) / 2 + Num2Integer(xr);
y := (frame.bottom + frame.top) / 2 + Num2Integer(yr);
{ Draw crosshair }
MoveTo(x - 4, y);
LineTo(x + 4, y);
MoveTo(x, y - 4);
LineTo(x, y + 4);
{ Draw arc }
FrameArc(frame, 0, angle);
end;
QD 510 - Color Manager Q&As
QuickDraw
Revised by: Developer Support Center September 1993
Written by: Developer Support Center October 1990
This Technical Note contains a collection of Q&As relating to a specific topic—questions you’ve sent the Developer Support Center (DSC) along with answers from the DSC engineers. While DSC engineers have checked the Q&A content for accuracy, the Q&A Technical Notes don’t have the editing and organization of other Technical Notes. The Q&A function is to get new technical information and updates to you quickly, saving the polish for when the information migrates into reference manuals.
Q&As are now included with Technical Notes to make access to technical updates easier for you. If you have comments or suggestions about Q&A content or distribution, please let us know by sending an AppleLink to DEVFEEDBACK. Apple Partners may send technical questions about Q&A content to DEVSUPPORT for resolution.
Using ResEdit to get Apple icon RGB values
Date Written: 1/7/92
Last reviewed: 6/14/93
I am trying to find the RGB values for Apple's standard icon colors. I can’t find this information in any documentation. Is this information available?
___
While the RGB values for the standard Apple icon colors (and other standard palette colors) are not explicitly documented, they are easy to obtain with ResEdit 2.1.1. In any resource file, create and open a new 'pltt' resource. Choose Load Colors from the pltt menu and pick Apple Icon Colors, and a standard palette will be created. Selecting a color will reveal its component values.
ResEdit 2.1.1 is available on the latest Developer CD Series disc and from APDA.
Macintosh Color Manager versus Palette Manager
Date Written: 1/1/90
Last reviewed: 6/14/93
When should the Macintosh Color Manager be used and when should the Palette Manager be used?
___
The Palette Manager is by far the friendlier and more versatile of the two if your application uses different colors than the default system colors. It provides all the functionality you need to customize and animate the colors in your application. You shouldn’t ever need to use the Color Manager unless you require custom color search and complement functions. When using the Palette Manager, applications will maintain their respective color environments safely as they move back and forth from foreground to background, and from one screen to another. Accomplishing this with the Color Manager calls is not worth the effort or very safe. For additional information, see the Palette Manager chapter in Inside Macintosh Volumes V and VI.
QD 515 - Color QuickDraw Q&As
QuickDraw
Revised by: Developer Support Center September 1993
Written by: Developer Support Center October 1990
This Technical Note contains a collection of Q&As relating to a specific topic—questions you’ve sent the Developer Support Center (DSC) along with answers from the DSC engineers. While DSC engineers have checked the Q&A content for accuracy, the Q&A Technical Notes don’t have the editing and organization of other Technical Notes. The Q&A function is to get new technical information and updates to you quickly, saving the polish for when the information migrates into reference manuals.
Q&As are now included with Technical Notes to make access to technical updates easier for you. If you have comments or suggestions about Q&A content or distribution, please let us know by sending an AppleLink to DEVFEEDBACK. Apple Partners may send technical questions about Q&A content to DEVSUPPORT for resolution.
New Q&As in this Technical Note:
Color QuickDraw not available to 68000 Macintosh models
Check ‘cicn’ ID if menu icons are tiny
Color QuickDraw and large pixmaps
Color QuickDraw not available to 68000 Macintosh models
Date Written: 2/4/93
Last reviewed: 3/17/93
Inside Macintosh Volume VI says Color QuickDraw is built into System 7, but when I use Gestalt to check for Color QuickDraw on a Macintosh PowerBook 100 running System 7, the call returns 0 in QDversion. What’s wrong?
___
The Color QuickDraw routines were written in 68020 assembly, meaning that only Macintosh models with 68020 and later CPUs have access to Color QuickDraw. Models such as the Macintosh SE, Macintosh Classic, and PowerBook 100 can use only Classic QuickDraw. However, this isn’t such a bad thing because the processing power required to run Color QuickDraw would be an enormous burden on the 68000 machines.
The bulk of Color QuickDraw routines reside in the larger ROMs of models using the 68020 and greater CPUs, thus taking the burden away from system software. However, if changes have to be made to Color QuickDraw (usually in the form of patches) they reside in system software.
Check 'cicn' ID if menu icons are tiny
Date Written: 2/16/93
Last reviewed: 7/2/93
The icons that appear in our application’s menu lists are very, very small. It looks like they’re 8 x 8 (scaled) instead of the standard 16 x 16. Can you tell from our test code why this is happening?
___
This was a wild one! What’s causing the weird behavior is that you have a 'cicn' resource with ID = 256 that’s smaller than 32 x 32. When the MDEF finds such an icon, it uses it to size the area to use for the menu icons. So, for your application the solution is either to change the 'cicn' to be larger (32 x 32) or to give it a different ID.
Color QuickDraw and large pixmaps
Date Written: 3/11/93
Last reviewed: 6/24/93
Somewhere in QuickDraw, a routine is using the 14th bit of the rowBytes field (the 15th is used to differentiate a pixmap from a bitmap) and it really shouldn’t. This is a problem now that it’s so easy to create large pixmaps of 4096 32-bit pixels or more. What’s causing the problem and what’s the recommended solution for handling large pixmaps?
___
This is a long story. It’s described in Inside Macintosh Volumes V and VI but we’ll summarize it here.
Color QuickDraw allows you to pass one of three addresses to CopyBits: the address of a bitmap, the address of a pixmap, or the address of the pixmap handle of a port. The first two conditions (bit and pixmap) are determined by bit 15 of rowBytes: if the bit isn’t set, the address points to a bitmap; otherwise, it points to a pixmap. The third case is the problem here. A color port’s portVersion field is expected to be set to 0xC000, so after deciding it’s dealing with a possible pixmap CopyBits checks bit 14 and if it’s set it assumes it got the address of the pixmap handle in a CGrafPort and proceeds to dereference it in order to get the real address of the pixmap.
Today’s applications have trouble with this because they must either refuse to create pixmaps as big as users want or cause crashes by confusing CopyBits into dereferencing the base address of the pixmap if rowBytes exceeds the established limit of less than 0x4000.
Engineering is studying future solutions. It’s possible that a future QuickDraw release will support pixmap with a rowBytes constant value indicating that the real rowBytes is contained in the planeBytes field instead. You might think of cases where this is also going to cause problems but the problems probably are less important than the limitation being overcome.
For the present, the solution depends on the conditions under which the problem affects you. If you’re writing an application, the solution could be to patch CopyBits and call DrawPicture. When CopyBits is called while a picture is being drawn the source is a pixmap; if rowBytes is too big, your application could split the job banding the image vertically until the resulting rowBytes values fall within range. With a little debugging it would be possible to find where CopyBits calls the bit/pixmap/port checking routine and bypass that, given that the actual routine doing the blitting doesn’t have problems dealing with pixmaps of less than 0x8000 rowBytes.
Another possibility is to call StdBits directly since it doesn’t mind dealing with larger than legal rowBytes. The problem here is that the destination is implied and the application has to make sure that everything is all right. Also if the destination spans multiple devices the application must divide the task, targeting each device at the time. See the DeviceLoop procedure in Inside Macintosh Volume VI for ideas on this.
To recap: The limitation of rowBytes is becoming increasingly painful, now that applications can easily create pixmaps (and PICTs) that exceed the limit of 0x4000. It’s possible for an application to patch CopyBits in order to work around this limitation but you have to decide what is appropriate for each set of conditions.
Animation speed on the Macintosh
Date written: 1/18/93
Last reviewed: 4/1/93
How can I get reasonably fast animation on Macintosh models? So far, I’ve created off-screen pixmaps with the image and mask, and an extra off-screen pixmap for use in restoring the original picture. However, CopyPix is still too slow. I had to write my own CopyPix routine in assembly, which works great. But I can’t help wondering how Apple expected fast animation to be accomplished.
___
You’re certainly right that the way to increase performance is by off-screen drawing and QuickDraw’s CopyBits procedure. The key information that’s useful to you is in the Macintosh Technical Note “Of Time and Space and _CopyBits.” This tech note covers some of the factors affecting CopyBits and what can be done to improve the speed of calling CopyBits.
Just to mention some of the factors that might improve the speed of calling CopyBits: Avoid color mapping, or even try faking out color mapping by setting your ctSeed to be the same. Alignment of pixels in the source map relative to their alignment to the destination pixel map can be important. If the source and destination rectangles are different sizes, CopyBits has to scale the copied image, which slows it down a lot. Also dithering and bit depth has effect on the speed of CopyBits.
Whereas QuickDraw often trades performance for flexibility, there are times we’d just as soon trade flexibility for performance. In these cases, we can achieve tremendous gains in speed by writing custom routines to draw to off-screen worlds. I recommend the article “Drawing in GWorlds for speed and versatility” (develop, issue 10) which shows you exactly how to do that.
Many developers want to go beyond the speed of QuickDraw. Writing directly to the screen can allow you to create faster animation and graphics than possible with QuickDraw. However, Apple has always maintained that writing to video memory is unsupported, since it may cause your application to break on future systems. If you write directly to the screen, your application will forfeit the use of many Toolbox managers and will put future compatibility at risk. Since most applications require the Window Manager and other basic Macintosh managers, writing to the screen is for only a few specialized applications, such as video games and some animation packages that compete on the quality and speed of graphics. The most important thing to remember is don’t write directly to the screen if you don’t have to. But if you do need to, an article that provides some guidelines is “Graphical Truffles: Writing Directly to the Screen,” develop, Issue 11.
Following are some additional articles that animation and game developers find useful:
• “Palette Manager Animation,” develop, Issue 5.
• “Using the Palette Manager Off-Screen,” develop, Issue 10.
• “QuickDraw’s CopyBits Procedure: Better Than Ever in System 7.0,” develop, Issue 6.
• “Graphical Truffles: Animation at a Glance,” develop, Issue 12.
Also, some sample code that can speed your development is given as SC.015.Offscreen and SC.016.OffSample (Dev CD Jan 93:Technical Documentation:Sample Code: Macintosh Sample Code).
Animation on the Macintosh does take some work. Nevertheless, we’ve seen some pretty amazing animations developed on the Macintosh.
Inside Macintosh Volume V PnPixPat & BkPixPat doc fix
Date written: 12/4/92
Last reviewed: 3/1/93
Inside Macintosh Volume V (page 103) says that when a PICT pattern opcode (for instance, 0x0012) comes along, and the pattern isn’t a dither pattern, the full pixMap data follows the old-style 8-byte pattern. The full pixMap data structure described on page 104 indicates that a pixMap starts with an unused long (baseAddr placeholder), followed by the rowBytes, bounds, and so on. However, looking at the Pict.r file on the October 1992 Developer CD, at the same opcode (BkPixPat == 0x0012), the first data field after the old-style pattern (hex string[8]) is the rowBytes field (broken down into three bitstrings). The baseAddr placeholder field isn’t there. Which is correct?
___
The Inside Macintosh documentation on pages V-103 and V-104 is wrong. The Pict.r file correctly describes the format of the PnPixPat and BkPixPat opcodes. So there shouldn’t be a baseAddr field in the pixMap record of a pattern as stored in the PnPixPat of a PICT. However, the baseAddr does occur in a 'ppat' resource as described on page V-79. Thanks for pointing out this discrepancy.
Disabling Macintosh Color QuickDraw for testing
Date written: 9/14/92
Last reviewed: 6/14/93
Is there an easy way to disable Color QuickDraw on a Macintosh? I want to do this for testing our application, to make sure it works correctly on a machine without Color QuickDraw.
___
There’s no easy, or perhaps even hard way to disable features built into the system software your particular machine requires. It’s designed to work well, not to be toggle-able.
Even the hard way isn’t a sure thing—trying to patch out all the Color QuickDraw traps could confuse the rest of the system software, which internally may use undocumented routines to accomplish its tasks.
The easiest way to test on non-Color QuickDraw machines is to get one. Fortunately, the machines without Color QD are the lowest end of the Macintosh price spectrum—such as the Macintosh Classic, PowerBook 100, and Macintosh SE. You can probably rent or borrow one of these if the prices don’t fit your current budget.
Using a Macintosh PICT file that’s larger than available memory
Date Written: 6/18/90
Last reviewed: 6/14/93
How can I read a 2 MB PICT file into only 1 MB of memory?
___
You can’t read it in since you don’t have enough memory, but drawing the picture contained in the file using a technique called “spooling” increases your chances of using a 2 MB PICT file with 1 MB memory. Spooling is documented in the Color QuickDraw chapter of Inside Macintosh Volume V (pages 88–89).
Getting a single scan line from a PICT file
Date Written: 6/18/90
Last reviewed: 6/14/93
Is there any way to obtain a particular scan line from a PICT file?
___
A PICT file may contain more than just pixmaps, so getting one scan line out of it is not possible. The file may also contain other elements that overlap, such as rects and arcs. The only way to obtain a single line is to draw it off-screen and then, once the whole image is in memory, you can go and study individual pixels.
Determining pixel depth from PICT files
Date Written: 6/20/90
Last reviewed: 9/17/91
How do you find out the pixel size of a PICT file on the disk?
___
A picture is by nature independent of depth. For example, you can have a picture containing DrawRects and LineTos and therefore lacking any info regarding depth.
On the other hand, if the picture you are looking at has pixmap opcodes in it, then each pixmap contains its own pixel size and in this case a picture can have a number of depths associated with it.
If you want to see the pixel size for each pixmap opcode in a picture, replace all the bottleneck routines and every time the bitsProc is called you can see the pixmap and get the info out. Since the picture is in a file, you can use the spooling technique described in the QuickDraw chapter in Inside Macintosh Volume V. Be ready to deal with multiple, possibly different, pixmaps as well as direct pixmaps if the picture was created under 32-bit QuickDraw.
”KnowsPICT,” on the Developer CD Series disc, extracts this kind of information. The System 7.0 Picture Utilities package gets this information too.
BitMapToRgn for non-Color QuickDraw Macintosh models
Date Written: 11/9/90
Last reviewed: 6/14/93
Is _BitmapToRegion available on any pre-System 7 non-Color QuickDraw configurations such as the Macintosh Classic, Plus, or SE? If not, is source or a library module available so that I don’t have to take the time and compatibility risk of rolling my own?
___
BitMapToRegion works on pre-color Macintosh systems. You can license BitMapToRegion from
Software Licensing
Apple Computer, Inc.
20525 Mariani Ave. MS:38-I
Cupertino, CA 95014
AppleLink: SW.LICENSE
Phone:(408) 974-4667
Macintosh QuickDraw pixel map stack requirements
Date Written: 12/3/90
Last reviewed: 5/21/91
What are the guidelines for determining how much of an image CopyBits can copy to a Macintosh pixel map at one time, given a particular set of characteristics for the source map and the destination map and given how much stack space is available? For example, say that we have an 8-bit-deep pixmap to be copied to a 32-bit-deep pixMap using the ditherCopy mode and expanded by a factor of 4, and we have 45K of stack space.
___
CopyBits’ stack requirement depends on the width of each scan line (rowBytes). The rule of thumb is that you need at least as much stack as the rowBytes value in your image (which can be huge with 32-Bit QuickDraw), with the following additional modifiers: Add an additional rowBytes for dithering; add an additional rowBytes for any stretching (source rect != dest rect); add an additional rowBytes for any color map changing; add an additional rowBytes for any color aliasing. The stack space you need is roughly five times the rowBytes of your image. In general, you’re better off processing narrower scan lines. Reducing the vertical size will not affect stack requirements. Narrow, tall bands (if you can use them) will reduce the stack requirements.
Color and non-Color QuickDraw trap dispatch differences
Date Written: 1/28/91
Last reviewed: 6/14/93
Why does a call to RGBForeColor cause a corruption of the stack without resulting in an unimplemented trap error on non-Color QuickDraw Macintosh systems?
___
The trap dispatcher on Color QuickDraw and non-Color QuickDraw machines are different. If you look at page 89 of Inside Macintosh Volume I, you’ll see the toolbox trap word format as it was in the days before Color QuickDraw. Bit 9 was “reserved for future use” and was ignored by the trap dispatcher, and so it was normally set to 0. That means that valid toolbox traps could either look like $A8XX or $A9XX as long as the auto-pop bit was turned off. Color QuickDraw machines have a trap dispatcher that uses that reserved bit to allow for more trap words, and therefore it has a much larger trap dispatch table. Color QuickDraw traps have that reserved bit set, so those traps look like $AAXX or $ABXX.
When a non-Color QuickDraw machine tests to see if a trap is implemented or not, it just checks the trap dispatch table to see if a routine is implemented for that trap or not. Because the reserved bit is ignored, trap words that look like $AAXX are treated as equivalent to $A8XX and trap words that look like $ABXX are treated as equivalent to $A9XX. The trap word for RGBForeColor is $AA14. If you call RGBForeColor on a non-Color QuickDraw machine, $AA14 is treated as $A814, which is the trap word for SetFractEnable. SetFractEnable is implemented on 128K ROM machines or greater, so no unimplemented trap error occurs.
If you look at recent DTS sample programs, such as the Utilities sample (SC.025.Utilities, which you can find on AppleLink in Developer Support and on the current developer CD), you’ll see a routine in Utilities.c called TrapExists. It takes into account the size of the trap dispatch table so that you can tell in one call whether a routine is implemented or not regardless of whether it’s a Color QuickDraw trap or not and regardless of what kind of Macintosh you’re running on.
Under system software version 7.0, the trap dispatcher is modified on non-Color QuickDraw machines so that many Color QuickDraw traps are implemented and work as well as they can in black and white.
Macintosh OpenCPicture 72-dpi calculation bug
Date Written: 2/12/91
Last reviewed: 6/14/93
The 32-Bit QuickDraw _OpenCPicture call incorrectly calculates the 72-dpi frame width if the height of the native resolution srcRect exceeds 910 dots. To work around this problem, I calculate the 72-dpi frame independently, and store it in the PicHandle returned by _OpenCPicture.
___
It’s a known bug that under Macintosh system software versions 6.0.5 and 6.0.7 with 32-Bit QuickDraw 1.2, OpenCPicture doesn’t properly calculate the right coordinate of the 72-dpi picFrame if the height of the srcRect (native resolution rectangle) multiplied by 72 exceeds $0000FFFF. That works out to a maximum height of 910 pixels, just as you found. This bug is fixed in System 7.0, but gestaltQuickdrawVersion returns $0220 both under system software versions 6.0.5 and 7.0, so you can’t tell whether the bug is fixed that way. Instead, you should use Gestalt with the gestaltSystemVersion selector. If the returned value is $0700 or greater, then let OpenCPicture handle the picFrame calculation; otherwise you should do the calculation yourself.
GetGWorldPixMap bug and workaround
Date Written: 3/12/91
Last reviewed: 6/14/93
Why does GetGWorldPixMap (when called on a Macintosh II, IIcx, or IIx running system software version 6.0.5 or 6.0.7 with 32-Bit QuickDraw 1.2) return a combination of the device field (two bytes) and the first two bytes of the portPixMap field? Is this a bug?
___
Your analysis of GetGWorldPixMap is exactly right: It doesn’t work correctly in system software version 6.0.5 and 6.0.7 with 32-Bit QuickDraw 1.2. It returns a value that’s two bytes before the value it’s supposed to return.
The solution is to use GWorldPtr->portPixMap instead of GetGWorldPixMap. It’s safe to do this, but you should use GetGWorldPixMap under System 7. Not only is the bug fixed there, but dereferencing the port is dangerous under System 7 because it may not be CGrafPort. Use Gestalt with the gestaltQuickdrawVersion selector to determine whether you can use GetGWorldPixMap. If Gestalt returns a value from gestalt8BitQD ($0100) through gestalt32BitQD12 ($0220), then GetGWorldPixMap either doesn’t exist or is the buggy version. If it returns gestalt32BitQD13 ($0230) or higher, then GetGWorldPixMap does exist and works correctly. Interestingly, GetGWorldPixMap can be called on a black-and-white QuickDraw machine under System 7. It returns a handle to a structure which should be treated as a BitMap structure, though there are some undocumented fields after the normal BitMap fields. To tell whether GetGWorldPixMap is available on a black-and-white QuickDraw machine, you must check the system software version by calling Gestalt with the gestaltSystemVersion selector. If it returns $0700 or higher, GetGWorldPixMap is available.
System 7 TextMode problem and workaround
Date Written: 6/12/91
Last reviewed: 8/13/91
Our application uses the TextMode (blend + mask) as documented in Inside Macintosh Volume V (blend is equal to the current ditherCopy constant) to make translucent text. Under System 7, this transfer mode causes garbage to appear when the text is drawn. Is there a way to work around the problem? Will there be a fix?
___
The problem you are seeing is due to the use of CopyDeepMask instead of the old-fashioned CopyBits to do the job. It is being studied now, and the hope is that it will work as advertised in a future release. One workaround is to render the text to an off-screen pixmap and then call CopyBits (using blendMode) to actually put it in the picture.
Using dithering and animation on the same Macintosh image
Date Written: 6/19/91
Last reviewed: 6/14/93
When setting up a dithered grayscale image for subsequent animation (to adjust brightness, for example), a conflict arises between the use of Palette animation and the ditherCopy CopyBits mode. This problem is demonstrated in the develop #5 GiMeDaPalette code sample: If you change srcCopy to ditherCopy in the CopyBits call, then run the program and select Animate, the resulting image is pure black and white, with what appears to be an attempt to dither with just the black-and-white color table entries (that are not reserved for animation).
This happens because ditherCopy tries to use the inverse table to do color matching, but when the image is animated, the inverse table colors are limited to just black and white.
To work around the problem, you can jump into the bottlenecks and when you see the PICT hitting the opcode for CopyBits, change the mode adding the ditherCopy constant. This way the dithering happens when you do the call to DrawPicture and not later on. This makes it possible to use dithering and animation on the same image.
Rendering color PICTs in a black-and-white environment
Date Written: 7/22/91
Last reviewed: 9/17/91
I want to be able to render a color PICT as a black-and-white image substituting patterns for colors. My images are pretty small and have fewer than 16 colors. What do you suggest as the easiest way?
___
One easy way is to take advantage of 32-Bit QuickDraw and System 7.0’s ditherCopy transfer mode modifier or flag (documented in Inside Macintosh Volume VI, page 17-17). Call DrawPicture into an off-screen pixmap with the pixel depth of the original color PICT. Then call CopyBits to copy the pixmap to the screen, with srcCopy + ditherCopy as the transfer mode. This will result in a nicely dithered image on the black-and-white end.
Under System 6 without 32-Bit QuickDraw, the solution is not nearly so cut and dried. One way might be to take advantage of the fact that DrawPicture goes through the QuickDraw bottlenecks for drawing. For each grafproc in your PICT, you’d intercept StdBits during DrawPicture and call your own dithering routine to examine the foreground color and set the pen pattern or fill pattern so that it has about the same lightness as the original color.
Well, this came out as a great sales pitch for writing a System 7-savvy app!
Highlighting ignored if foreground same as background color
Date Written: 8/7/91
Last reviewed: 6/14/93
Under System 7, but not System 6, HiliteMode doesn’t work when the foreground and background colors are similar. Is this a bug?
___
Yes, it’s a bug. The problem you encounter exists whenever the background and foreground color map to the same color table index. If the foreground color is the same as the background color, highlighting is ignored. Therefore, you should always make sure the foreground and background colors are different when using HiliteMode.
Gestalt 'qdrw' selector bug and workaround
Date Written: 8/1/91
Last reviewed: 6/14/93
Why does Gestalt tell me I have Color QuickDraw features on a non-Color QuickDraw machine?
___
The gestaltQuickdrawFeatures ('qdrw') selector, used for determining your system’s Color QuickDraw features, has a bug that causes it to tell you incorrectly that noncolor machines have color. The fix is quite simple: Gestalt has another selector, gestaltQuickdrawVersion ('qd '), which simply returns the QuickDraw version number. This version number is < gestalt8BitQD for classic QuickDraw and >= gestalt8BitQD for Color QuickDraw (see Inside Macintosh Volume VI, page 3-39, for more information). The trick is to ask Gestalt for the QuickDraw version first; once you’ve determined that you have Color QuickDraw, the 'qdrw' selector is OK to use to find out specifics.
GetPixelsState is slow sometimes
Date Written: 8/27/91
Last reviewed: 6/14/93
Why do I sometimes see incredible slowdowns under System 7.0 when calling either GetPixelsState or LockPixels (I’m not sure which) for the PixMapHandle of a GWorld allocated in temporary memory?
___
GetPixelsState takes an arbitrary amount of time since it makes a call to RecoverHandle to get the handle pointing to the baseaddr. Therefore, the slowdown you see as actually due to the call to RecoverHandle, which is slow because it must traverse the heap to find the pointer to the baseaddr. LockPixels is not responsible for the slowdown because it does not make call to any traps that could take an extended amount of time.
OpenCPicture and PICTs other than 72 dpi
Date Written: 10/2/91
Last reviewed: 6/14/93
Can I use OpenCPicture to create PICTs with a higher resolution than 72 dots per inch (dpi)?
__
There’s good news and bad news: The good news is that you’re on top of the situation, which means the bad news is that there aren’t better ways to do what you want to do, mostly. Here’s the scoop:
You can use vRes and hRes in pictures opened with OpenCPicture to tell QuickDraw it’s not a 72-dpi picture, and as long as the application that receives the picture uses DrawPicture to image it, QuickDraw will Do The Right Thing—scaling it on the screen to 72 dpi instead of making it humongously large. Unfortunately, this way you lose hairlines; if you print such a picture to a 72-dpi grafPort (like the LaserWriter driver normally returns), you’ll get 1/72-inch lines instead of 1/300-inch lines as you probably want.
(This can work correctly, but the receiving application has to notice that your picture is bigger than 72 dpi and ask PrGeneral to increase the resolution of the printing grafPort accordingly, and this doesn’t always or often happen.)
No System 7 QuickDraw alpha channel support
Date Written: 10/23/91
Last reviewed: 6/14/93
How can I directly access the alpha channel (the unused 8 bits in a 32-bit direct pixel using QuickDraw) under System 7? Under System 6 it was easy, but under System 7’s CopyBits the alpha channel works with srcXor but not with srcCopy.
___
With the System 7 QuickDraw rewrite, all “accidental” support for the unused byte was removed, because QuickDraw isn’t supposed to operate on the unused byte of each pixel. QuickDraw has never officially supported use of the extra byte for such purposes as an alpha channel. As stated in Inside Macintosh Volume VI, page 17-5, “8 bits in the pixel are not part of any component. These bits are unused: Color QuickDraw sets them to 0 in any image it creates. If presented with a 32-bit image—for example, in the CopyBits procedure—it passes whatever bits are there.”
Therefore, you cannot rely on any QuickDraw procedure to preserve the contents of the unused byte, which in your case is the alpha channel. In fact, even CopyBits may alter the byte, if stretching or dithering is involved in the CopyBits, by setting it to 0. Your alternatives are not to use the unused byte for alpha channel storage since the integrity of the data cannot be guaranteed, or not to use QuickDraw drawing routines that can alter the unused byte.
BitsToRgn and MPW BitMapToRegionGlue
Date Written: 10/29/91
Last reviewed: 6/14/93
Which version of the system software first contained the call BitsToRgn? Is there a workaround for this call if my users have an earlier version of system software?
___
The call BitmapToRegion was introduced with 32-Bit QuickDraw and became fully documented in Volume VI of Inside Macintosh, which is primarily System 7 information. However, since the differences between System 7’s QuickDraw and 32-Bit QuickDraw are minor, most of System 7’s QuickDraw routines are available in system software prior to System 7.0 using the 32-bit QuickDraw INIT.
To check to see if a system contains 32-bit QuickDraw, you can use the following snippet of code:
/* Find out if GWorlds and CQD are implemented on this machine */
If you are using MPW as a development platform, MPW has a library call you can use that will allow you to use the routine regardless of whether or not 32-bit QuickDraw exists. The glue routine is called BitMapToRegionGlue() and is available to MPW users. Substitute this call for BitMapToRegion calls and the glue code will take care of patching in the proper code if 32-bit QuickDraw does not exist. If you’re using Think C, you can use the oConv utility to convert the MPW object file into a Think C usable format.
Ensuring even rowBytes for 'cicn' resources
Date Written: 12/4/91
Last reviewed: 6/14/93
Is there any way to force bitmaps and masks within a 'cicn' resource to have an even rowBytes (using ResEdit)? I want to avoid duplicating icon bitmaps—one for color systems set to B&W and one for B&W systems—to reduce program size as well as development and maintenance costs. The bitmaps in the 'cicn' can also be sized specifically to the task, whereas the old B&W icons are of a fixed size and contain no sizing information. It’s simple enough to read in a 'cicn' and extract the bitmap. The problem is that on a 68000 (no Color QuickDraw), if rowBytes is odd, an odd address trap results.
___
There isn’t any way to get ResEdit itself to create bitmaps with even rowBytes for 8 x 8 'cicn' resources, but here are few suggestions:
You could process your 'cicn' resources first, so that they have bitmaps as you require them. To alter the resource with a quick little program would be trivial, especially given that the bitmap data sits last in the 'cicn'. All you’d need to do is expand the bitmap image data by padding each line to an even length and then changing the rowBytes value. Or you could de-rez the 'cicn's and patch them with a text editor, either by hand or with a search-and-replace script of some kind.
I have two gray-colored pixmaps that I wish to blend together; one is on the screen, the other in an off-screen pixmap. I use CopyBits to copy the off-screen to the screen, but it does not seem to blend them. Instead, it seems to match the colors of the screen bitmap to the closest colors in some table, thus having the effect of reducing the number of colors displayed on the screen bitmap. Any suggestions?
___
There are two distinct questions here: 1) Why ain’t it blending? and 2) What’s this banding for? The first problem is almost certainly because OpColor isn’t set properly. This is a third, implicit, operand on several arithmetic graphics operations, including blend. For blend, it describes the proportions to mix the source and destination colors in the blend. For an equal mix, you should set this color to a halfway gray. (Call OpColor() with a color where red, green, and blue all equal $8000.) This effect is described in the description of the blend mode on page 60 of Inside Macintosh Volume V. Unfortunately, the initial value for OpColor is black (0,0,0), so you were seeing no mixing of your off-screen data.
The second half of your question is why you’re getting a banding effect. (When you fix the above problem, you’ll still get banding.) Unfortunately, the arithmetic modes are constrained by the size of the inverse table. As your screen no doubt uses the default 4-bit inverse table, you’ll find that you’ll get only 2^4 = 16 levels of gray. If you enlarge your screen’s inverse table to 5 bits, the maximum allowable, you’ll still only get 32 gray levels. (To do this, set the gdResPref field in the GDevice to 5, then call MakeITable().) The only way to get a fully-gradual, great-looking effect is to do all the work off-screen in 24-bit deep pixmaps, and then copy it to the screen. Because they can operate directly on colors, rather than having to work through the intermediary of color indices, direct pixmaps are not limited by inverse tables (in fact, they don’t even have real inverse tables). You could use 16-bit pixmaps, but they only provide 32 grays (having only 5 bits for each component), so this wouldn’t be any better than increasing the size of the inverse table.
Icon dimming under System 7 and System 6
Date Written: 1/6/92
Last reviewed: 6/14/93
When you bring up the Finder windows under System 7 on a color system and click a control panel item icon, it paints itself that fancy gray. How can I get that effect?
___
To get the fancy System 7 icon dimming to work in your program, read the Macintosh Technical Note “Drawing Icons the System 7 Way,” and use the icon-drawing routines contained in it. The routines show how to use the Icon Toolkit, which is what the Finder uses. If you want the same effect under System 6, you’ll have to emulate the dimming of the icons via QuickDraw; the IconDimming sample code in the Snippets folder on the Developer CD Series disc shows how to do this.
QuickDraw out of memory if debugger invoked by “Jackson”
Date Written: 3/11/92
Last reviewed: 6/14/93
I am getting a strange bug in which the Macintosh debugger is being invoked by an A-trap marked “Jackson” when I call SetCCursor in certain situations and a second monitor is hooked up. The cursor structure being passed appears to be valid. I’ve also been crashing unexpectedly in this same spot for the past few weeks. I assume Jackson is some kind of error assertion that was left in System 7’s Color QuickDraw code. What gives?
___
Jackson was a code name for 32-Bit QuickDraw. The trap you refer to is in fact never called; it’s not supposed to be encountered by you ever. The trap is reserved for Apple to use in future versions of Color QuickDraw. If you examine the code directly preceding the _Debugger, you will notice that it is doing
MOVEQ #$19,D0
JSR ([$1524])
which for you and me is
MoveQ #25,D0 ; say that memory is full…
_SysError ; and call syserror
the line following would be...
_Debugger ; Hey! sysError came back! Better drop into the debugger
What’s all this tell you? You have a debugger installed that is rts’ing from the SysError vector (you aren’t supposed to return from SysError normally), or you have installed your own SysError vector which is rts’ing. At any rate, if you examine the code directly following the debugger statement and see what it does, you might imagine the source code looks something like this:
MemFull MoveQ #25,D0 ; say that memory is full...
_SysError ; and call syserror
; If it returns better go into the debugger since its not supposed to return
_Debugger ; Hey! sysError came back!
;
CallNewHand _NewHandle
bne.S MemFull ; could not get the memory, just die
rts
What’s happening is that you’re running out of memory somehow (several places call MemFull, not just the above place), so you’d need to use a stack crawl to figure out how you got there. But, the bottom line, QuickDraw has run out of memory and cannot continue; it tried to put up a system error dialog to tell the user and for some reason the machine did not get restarted and the SysError vector returned. You are now in your debugger, since QuickDraw put up the system error dialog because it could not continue.
ditherCopy not supported on LaserWriter or ImageWriter
Date Written: 5/31/91
Last reviewed: 11/6/91
ditherCopy is not supported on LaserWriters or ImageWriters. On a LaserWriter, ditherCopy gets misinterpreted and inverts the image. On an ImageWriter it’s treated as a srcCopy. The ImageWriter driver doesn’t support color grafPorts, which is the only way to do the pixel image required for ditherCopy. Use srcCopy instead for both printers.
Macintosh Color QuickDraw CalcCMask and SeedCFill clarified
Date Written: 1/1/90
Last reviewed: 11/21/90
I’m having trouble using CalcCMask and SeedCFill. What am I doing wrong?
___
There is some confusion regarding the use of the Macintosh Color QuickDraw routines CalcCMask and SeedCFill, which are analogous to the older CalcMask and SeedFill. Much of the confusion was caused by early documentation errors. Be sure you have the release version of Volume 5 of Inside Macintosh and version 2.0 or later of the MPW interface files.
The correct interface for CalcCMask is:
PROCEDURE CalcCMask(srcBits, dstBits: BitMap;
srcRect, dstRect: Rect;
seedRGB: RGBColor;
matchProc: ProcPtr;
matchData: LongInt);
The correct interface for SeedCFill is:
PROCEDURE SeedCFill(srcBits, dstBits: BitMap;
srcRect, dstRect: Rect;
seedH, seedV: INTEGER;
matchProc: Ptr;
matchData: LongInt);
Each routine calculates a one-bit deep bitmap representing either the mask or the fill area depending upon the routine. In both cases, the source bitmap may be either a bitmap or a pixmap, but the destination must be a bitmap, because it must have a depth of one-bit.
It is difficult to pass a pixmap for the source parameter because of Pascal’s type checking. To get around this difficulty, you can declare a new type:
BitMapPtr = ^BitMap
then use it to coerce the pixmap as follows:
SeedCFill(BitMapPtr(@myPixMap)^, ...);
If you have a PixMapHandle, do the following:
SeedCFill(BitMapPtr(myPixMapHandle^)^, ...);
If you are using a grafPort (or a window), you can pass myWindow^.portBits and not have to worry about whether the port uses a bitmap or a pixmap.
Most of the other parameters are explained in detail in Inside Macintosh. To use the matchProc and the matchData parameters, though, you need more information.
As stated in Inside Macintosh, the matchProc parameter is a pointer to a routine that you would like to use as a custom SearchProc. To better understand how this is used, it is helpful to know how SeedCFill and CalcCMask actually work.
Both routines start by creating a temporary bitmap which, by definition, is one bit deep. The source pixmap (or bitmap) is then copied to the temporary bitmap using CopyBits. This copy causes the image to be converted to a depth of one-bit. Now with a normal black-and-white image, the standard CalcMask or SeedFill routine is used to generate the destination bitmap.
Most of the real work is done in the original call to CopyBits, which maps the pixmap image to a monochrome bitmap equivalent. For each color in the source pixmap, CopyBits will map it to either black or white. Which colors map to black and which ones to white is determined by the SearchProc.
SeedCFill installs a default SearchProc that maps all colors to black except for the color of the pixel at (seedH,seedV). SeedFill then calculates as usual the fill mask for the white bits .
The default SearchProc for CalcCMask maps all colors to white except the color passed in the seedRGB parameter. The seedRGB parameter, then, would be the color of the item that you wanted to “lasso.”
But suppose you want to fill over all colors that were shades of green, not just the particular shade of green at (seedH,seedV). Or maybe you want to fill over all colors that are lighter than 50 percent brightness. Or maybe you want to use dark colors as edge colors for CalcCMask. To do such things, you need to pass a pointer to your own SearchProc in the matchProc parameter.
Because your matchProc is just a custom search procedure for the Color Manager, it should be declared as one, but Volumes I–V of Inside Macintosh have documented this routine incorrectly. The correct declaration for a custom SearchProc is as follows:
FUNCTION SearchProc(VAR RGB: RGBColor;
VAR result: LongInt) : Boolean;
Normally, as each SearchProc is installed, it is added to the head of the SearchProc chain, so that it is called before all of the other ones that were already installed. When a SearchProc is installed, it can do one of three things:
1. Completely ignore the call by returning FALSE and not modifying any of the input parameters;
2. Completely handle the call by setting the result parameter to be the index into the color table that matches (according to your rules) the RGB parameter. In that case, the SearchProc returns TRUE;
3. Partially handle the call by modifying the RGB parameter, then returning FALSE.
In cases 1 and 3, the Color Manager continues down the SearchProc chain until it finds one that returns TRUE. If none of the custom routines handle the call, then the built-in default routine is used. In case 3, you can change the RGB color that is being matched. For example, if you want all shades of green to map to pure green, modify the RGB color, then return FALSE, letting the Color Manager find the index of that green in the color table.
In case 2, you return TRUE to indicate that you handled the call, and you return the color table index in the result parameter. The Color Manager then uses that index. For example, if you want to substitute white for all colors that can’t be matched exactly in the color table, then each time you get called you either return the index into the color table of the exact color, or 0 (which is the index for white) for all other colors.
A custom SearchProc for SeedCFill and CalcCMask should always return TRUE because the default Color Manager SearchProc usually doesn’t make sense. Because SeedCFill and CalcCMask are using CopyBits to copy to a 1-bit bitmap, you need to set the result to be either 0 or 1 (the only possible values in a 1-bit bitmap). A result of 0 is white, and a result of 1 is black.
All colors for SeedCFill that should be “filled over” would generate a result of 0 (white), and all colors that stop the fill generate a 1 (black). SeedFill is then called to fill the white area. All colors for CalcCMask that you want to form boundaries should generate results of 1 (black).
When your SearchProc gets called, the gdRefCon field of the current GDevice (theGDevice^^.gdRefCon) contains a pointer to the following record:
matchRec = RECORD
red: Integer;
green: Integer;
blue: Integer;
matchData: LongInt;
END;
The red, green, and blue parameters for SeedCFill are the values of the color of the pixel at (seedH,seedV). For CalcCMask, they are the fields from the seedRGB parameter. Your SearchProc can use this information to decide which colors are “fill-over” colors and which colors are “boundary” colors. For example, if you always set (seedH,seedV) to be the mouse point, your SearchProc then bases its decisions using the color of the pixel under the cursor. For example, the user clicks a shade of green, so all shades of green get filled over.
The matchData field contains the value that you passed into the SeedCFill or CalcCMask routines in the matchData parameter. The use of this field is completely user-defined. For example, since your SearchProc routine may be a separate module, you might want to use this field to pass a handle to your variables. This field can contain a handle, a pointer, a long integer, or whatever; or you can just ignore this field altogether.
Warning: There are some features of CalcCMask and SeedCFill you should be aware of. To understand them, you should be familiar with the use of CalcMask and SeedFill, which are described in the QuickDraw chapter of Inside Macintosh Volume IV.
CalcCMask and SeedCFill both use a parameter set that is very similar to the one used by CopyBits. CalcMask and SeedFill, however, are a different story. Instead of passing bitmaps and rectangles to SeedFill and CalcMask, these routines use an unusual set of parameters that describe the memory to be operated upon in terms of pointers, height, width, and offsets to the next row (rowBytes). Although these parameters are fairly easy to calculate, there are some limitations.
The most restrictive limitation is that the width of the rectangle used must be an even multiple of 16 bits. This limitation exists because the width of the rectangle is passed to SeedFill and CalcMask as a number of words (2 bytes). When calculating this parameter, SeedCFill and CalcCMask round down to an even word boundary. This rounding means that the rectangles you pass to CalcCMask and SeedCFill should be an even multiple of 16 pixels in width. If they are not, then the rightmost portion of the mask will be garbage.
To figure out the color of the pixel at (seedH,seedV), SeedCFill calls GetCPixel. GetCPixel finds the color of the pixel at (h,v) in the current port. Therefore, if you pass a pixmap that is not the pixmap of the current port you will get bizarre results. In other words, seedH and seedV are expressed in the local coordinates of the current port, not the coordinate of the source pixmap.
You have two methods to make it work. First, always pass the pixmap of the current port as the source parameter. If you are using an off-screen pixmap, it is a good idea to have an associated port for it, and then call SetPort, passing it a pointer your off-screen port, before you call SeedCFill.
The second method involves letting SeedCFill get some wrong value for the color at (seedH,seedV) then using your own custom SearchProc to do the real work. The default SearchProc for SeedCFill relies on getting the correct color, but your SearchProc doesn’t have to.
SeedCFill also makes the assumption that the seedH and seedV parameters are in the local coordinate system of the destination bitmap. This assumption comes into play when SeedCFill calculates the seedH and seedV parameters for SeedFill.
All this means that SeedCFill only works correctly if the source pixmap, destination pixmap, and current port all use the same coordinate system. Because of the above problem, this is almost automatic since the current port’s portRect and the bounds of the source pixmap have to be the same anyway.
The easiest way to make all this work is to have your main port be an even multiple of 16 pixels wide. Then, make sure that your source and destination structures (pixmap or bitmap) are all the same size and all have origins of (0,0).
Macintosh PICT color picture file format
Date Written: 1/1/90
Last reviewed: 6/14/93
Is there a general file format for color pictures that is common to all of the color paint programs? If so, where is it documented?
___
Apple supports (and encourages developers to support) one file type for pictures: the PICT file type. Most paint-type programs handle PICT files.
A PICT file is composed of two parts in its data fork; the first 512 bytes are for the file header, which contains application-dependent information. You have to contact the individual publishers to find out their particular data structures. For example, you can contact Claris Technical Support at AppleLink CLARIS.TECH or (415) 962-0371 for the file header MacDraw writes to its files.
The rest of the data in the file is picture data as created by Macintosh QuickDraw with OpenPicture. You can find the information about this data in Volume V of Inside Macintosh (pages 84–105); this section also shows how to read/write PICT files.
You can also check the Macintosh Tech Note “Displaying Large PICT Files” for more details on the subject.
X-Refs:
DTS Macintosh Technical Note “QuickDraw’s Internal Picture Definition”
DTS Macintosh Technical Note “Displaying Large PICT Files”
Mac pixmap is clipped to visRgn defined by screenBits.bounds
Date Written: 1/1/90
Last reviewed: 11/21/90
I’m drawing into a large off-screen bitmap (pixmap), but anything drawn outside the 640- by 480-pixel Macintosh screen area doesn’t get written to the pixmap. Why not?
___
When you create a new port with OpenPort or OpenCPort the visRgn is initialized to the rectangular region defined by screenBits.bounds (IM I:163). If your port has a large portRect, any drawing will be clipped to the visRgn and you will lose any drawing outside of the screenBits.bounds rectangle.
To correct this set the visRgn of the port to coincide with your port’s portRect after creating the port.
Also note that OpenPort initializes the clipRgn to a wide-open rectangular region (-32768, -32768, 32767, 32767). Some operations, like OpenPicture, can fail with this setup, so try setting clipRgn to a smaller rectangle.
X-Refs:
DTS Macintosh Technical Note “Pictures and Clip Regions”
DTS Macintosh Technical Note “Drawing into an Off-Screen Pixel Map”
Using Macintosh System 7 OpenCPicture for higher resolution
Date Written: 1/1/90
Last reviewed: 6/14/93
We want to use OpenCPicture for higher resolution, not for color per se. Can OpenCPicture in System 7 be used with non-Color as well as Color QuickDraw Macintosh computers?
___
Yes, with System 7, OpenCPicture can be used to create extended PICT2 files from all Macintosh computers. Under System 6.0.7 or later, you must test for 32-Bit QuickDraw before using OpenCPicture. You can do this by calling Gestalt with the gestaltQuickdrawVersion selector. If it returns gestalt32BitQD or greater, then 32-Bit QuickDraw is installed.
How to identify 32-Bit QuickDraw version
Date Written: 1/1/90
Last reviewed: 6/14/93
How can my program find out which version of Macintosh 32-Bit QuickDraw is running?
___
The following code snippet demonstrates how to use the Gestalt Manager to determine which version of 32-Bit QuickDraw is installed. There is no way to determine the version of 32-Bit QuickDraw before Gestalt. For 32-Bit QuickDraw version 1.2, Gestalt returns 2.2. Inside Macintosh Volume VI describes the Gestalt Manager in detail.
#defineTRUE0xFF
#defineFALSE0
#define Gestalttest0xA1AD
#define NoTrap0xA89F
main()
{
OSErrerr;
longfeature;
if ((GetTrapAddress(Gestalttest) != GetTrapAddress(NoTrap))) {
err = Gestalt(gestaltQuickdrawVersion, &feature);
if (!err) {
if ((feature & 0x0f00) == 0x0000)
printf ("We have Original QuickDraw version 0.%x\n", (feature & 0x00ff));
else if ((feature & 0x0f00) == 0x0100)
printf ("We have 8 Bit QuickDraw version 1.%x\n", (feature & 0x00ff));
else if ((feature & 0x0f00) == 0x0200)
printf ("We have 32 Bit QuickDraw version 2.%x\n", (feature & 0x00ff));
else
printf ("We don't have QD\n");
}
else
printf ("Gestalt err = %i\n",err);
}
else
printf ("No Gestalt\n");
}
Macintosh QDError function under System 6 and System 7
Date Written: 1/1/90
Last reviewed: 12/7/90
Under what System 7 and System 6 conditions is it legal to call the Macintosh QDError function?
___
Under System 7, QDError can be called from all Macintosh computers. (System 7 supports RGBForeColor, RGBBackColor, GetForeColor, and GetBackColor for all Macintosh computers as well.) On a non-Color QuickDraw Macintosh, QDError always returns a “no error.” Under System 6, QDError cannot be used for non-Color QuickDraw Macintosh systems.
Macintosh CopyBits transfer modes changed for System 7
Date Written: 1/1/90
Last reviewed: 6/14/93
Why do some Macintosh CopyBits transfer modes produce different results for System 7 than for System 6?
___
Under System 6, the srcOr, srcXor, srcBic, notSrcCopy, notSrcOr, notSrcXor, and notSrcBic transfer modes do not produce the same effect for a 16- or 32-bit (direct) pixel map as for an 8-bit or shallower (indexed) pixel map. With Color QuickDraw these classic transfer modes on direct pixel maps aren’t color-based; they’re pixel-value-based. Color QuickDraw performs logical operations corresponding to the transfer mode on the source and destination pixel values to get the resulting pixel value.
For example, say that a multicolored source is being copied onto a black-and-white destination using the srcOr transfer mode, and both the source and destination are 8 bits per pixel. Except in unusual cases, the pixel value for black on an indexed pixel map has all its bits set, so an 8-bit black pixel has a pixel value of $FF. Similarly, the pixel value for white has all its bits clear, so an 8-bit white pixel has a pixel value of $00. CopyBits takes each pixel value of the source and performs a logical OR with the corresponding pixel value of the destination. Using OR to combine any value with 0 results in the original value, so using OR to combine any pixel value with the pixel value for white results in the original pixel value. Using OR to combine any value with 1 results in 1, so using OR to combine any pixel value with the pixel value for black results in the pixel value for black. The resulting image shows the original image in all areas where the destination image was white and shows black in all areas where the destination image was black.
Take the same example, but this time make the source and destination 32 bits per pixel. The direct-color pixel value for black is $00000000 and the direct-color pixel value for white is $00FFFFFF. CopyBits still performs a logical OR on the source and destination pixel values, but notice what happens in this case. Using OR to combine any source pixel value with the pixel value for white results in white, and using OR to combine any source pixel value with the pixel value for black results in the original color. The resulting image shows the original image in all areas where the destination image was black and shows white in all areas where the destination image was white—roughly the opposite of what you see on an indexed pixel map.
The newer transfer modes addOver, addPin, subOver, subPin, adMax, and adMin work consistently at all pixel depths, and often, though not always, correspond to the theoretical effect of the old transfer modes. For example, the adMin mode works similarly to the srcOr mode on both direct and indexed pixel maps. Also, 1-bit deep source pixel maps work consistently and predictably regardless of the pixel depth of the destination even with the old transfer modes.
Under system software version 7.0, the old transfer modes now perform by calculating with colors rather than pixel values. You’ll find that transfer modes like srcOr and srcBic work much more consistently even on direct pixel maps.
Which QuickDraw versions support SetEntries
Date Written: 3/3/92
Last reviewed: 6/14/93
I’m calling SetEntries to update the on-screen CLUT. Who implements this call? Does 32-Bit QuickDraw? In other words, does the 32-Bit QuickDraw INIT need to be around for this to work? What about monochrome machines?
I’m creating off-screen buffers by hand instead of using GWorlds. Is this the proper way of doing off-screen buffering when we don’t want to require the user to have 32-Bit QuickDraw?
___
SetEntries is part of the Color Manager, which exists with all Color QuickDraw versions. A good rule of thumb to follow is that if it is documented in Inside Macintosh Volume V, you don’t need 32-Bit QuickDraw to use it. Inside Macintosh Volume V documents standard Color QuickDraw. SetEntries does not work on monochrome Macintosh models, including the Classic II, SE, and PowerBooks.
Off-screen buffering: You should always use GWorlds if they exist; use Gestalt to test for them. This will assure that you can take advantage of the latest speed improvements. It is important to remember that under System 7 NewGWorld and accompanying calls are present in all Macintosh computers including black-and-white systems such as Classic and PowerBook 100 systems.
Macintosh pixel map maximum rowBytes change
Date Written: 4/22/91
Last reviewed: 6/14/93
The Color QuickDraw section of Inside Macintosh Volume VI states that the restriction on the rowBytes field in a pixmap has been relaxed from $2000 to $4000. When did this happen? Is it true for all 32-Bit QuickDraw versions? This affects our user configuration recommendations.
___
The maximum rowBytes extension to $3FFE or less applies only to 32-bit QuickDraw. Using pixmaps with rowBytes greater than $1FFE when 32-bit QuickDraw is not present is likely to cause problems such as garbage images or system crashes. Remember that 32-bit QuickDraw is always present under System 7.0 or higher.
Use assembly to flip a 24-bit off-port color pixmap
Date Written: 5/7/91
Last reviewed: 7/25/91
What’s the best approach to horizontally flip a 24-bit off-port color pixmap?
___
Unfortunately, you won’t be able to use CopyBits for this kind of procedure; you’ll have to write your own routine to move each pixel. I’d suggest doing this in assembly language to squeeze the best possible performance out of your code.
Why PlotCIcon requires GetCIcon instead of Get1Resource
Date Written: 4/26/91
Last reviewed: 6/17/91
Why do I have to use GetCIcon(resID) instead of Get1Resource('cicn',resID) forPlotCIcon to work correctly?
___
You apparently thought something that, at first, I thought also: that GetCIcon(resID) is just a utility routine that translates to Get1Resource('cicn',resID). However, this is not the case; GetCIcon not only gets the 'cicn' resource, but it also performs some minor surgery on the results, fills in some placeholder fields in the resource data, and the like. Basically, PlotCIcon can’t work without the things that GetCIcon does.
How Macintosh system draws small color icons
Date Written: 3/31/92
Last reviewed: 6/14/93
The code I added to my application’s MDEF to plot a small icon in color works except when I hold the cursor over an item with color. The color of the small icon is wrong because it’s just doing an InvertRect. When I drag over the Apple menu, the menu inverts behind the icon but the icon is untouched. Is this done by brute force, redrawing the small icon after every InvertRect?
___
The Macintosh system draws color icons, such as the Apple icon in the menu bar, every time the title has to be inverted. First InvertRect is called to invert the menu title, and then PlotIconID is called to draw the icon in its place. The advantage of using PlotIconID is that you don’t have to worry about the depth and size of the icon being used. The system picks the best match from the family whose ID is being passed, taking into consideration the target rectangle and the depth of the device(s) that will contain the icon’s image.
The Icon Utilities call PlotIconID is documented in the Macintosh Technical Note “Drawing Icons the System 7 Way”; see this Note for details on using the Icon Utilities calls.
Spooling and preserving Macintosh QuickDraw pixmap depth
Date Written: 2/11/92
Last reviewed: 6/14/93
When a picture that contains a pixmap is spooled into a window, how and when is the depth of the pixmap in the picture converted to the depth of the screens the window is on?
___
When a picture is spooled in, if QuickDraw encounters any bitmap opcode, it allocates a pixmap of the same depth as the data associated with the bitmap opcode, expands the data into the temporary pixmap, and then calls StdBits. StdBits is what triggers the depth and color conversions as demanded by the color environment (depth, color table, B&W settings) of the devices the target port may span (as when a window crosses two or more screens).
If there’s not enough memory in the application heap or in the temporary memory pool, QuickDraw bands the image down to one scan line and calls StdBits for each of these bands. Note that if you’re providing your own bitsProc, QuickDraw will call it instead of StdBits.
This process is the same when the picture is in memory, with the obvious exception that all the picture data is present; the color mapping occurs when StdBits does its stuff.
Determining the resolution of a PICT
Date Written: 6/10/92
Last reviewed: 6/14/93
In a version 2 picture, the picFrame is the rectangular bounding box of the picture, at 72 dpi. I would like to determine the bounding rectangle at the stored resolution or the resolution itself. Is there a way to do this without reading the raw data of the PICT resource itself?
___
With regular version 2 PICTs (or any pictures), figuring out the real resolution of the PICT is pretty tough. Applications use different techniques to save the information. But if you make a picture with OpenCPicture, the resolution information is stored in the headerOp data, and you can get at this by searching for the headerOp opcode in the picture data (it’s always the second opcode in the picture data, but you still have to search for it in case there are any zero opcodes before it). Or you can use the Picture Utilities Package to extract this information.
With older picture formats, the resolution and original bounds information is sometimes not as obvious or easily derived. In fact, in some applications, the PICT’s resolution and original bounds aren’t stored in the header, but rather in the pixel map structure(s) contained within the PICT.
To examine these pixmaps, you’ll first need to install your own bitsProc, and then manually check the bounds, hRes, and vRes fields of any pixmap being passed. In most cases the hRes and vRes fields will be set to the Fixed value 0x00480000 (72 dpi); however, some applications will set these fields to the PICT’s actual resolution, as shown in the code below.
Revised by: Developer Support Center September 1993
Written by: Developer Support Center June 1993
This Technical Note contains a collection of Q&As relating to a specific topic—questions you’ve sent the Developer Support Center (DSC) along with answers from the DSC engineers. While DSC engineers have checked the Q&A content for accuracy, the Q&A Technical Notes don’t have the editing and organization of other Technical Notes. The Q&A function is to get new technical information and updates to you quickly, saving the polish for when the information migrates into reference manuals.
Q&As are now included with Technical Notes to make access to technical updates easier for you. If you have comments or suggestions about Q&A content or distribution, please let us know by sending an AppleLink to DEVFEEDBACK. Apple Partners may send technical questions about Q&A content to DEVSUPPORT for resolution.
New Q&As in this Technical Note:
CWNewColorWorld CWord parameter
CMProfileResponse data structure
CWNewColorWorld CWorld parameter
Date Written: 2/22/93
Date reviewed: 7/2/93
Can I assume that the value of the ColorSync CWorld parameter returned by the CWNewColorWorld routine isn’t null if the routine was successful? I’d like to determine whether a color world exists by checking the variable for null.
___
You can assume that if no error is returned by CWNewColorWorld, the CWorld parameter will be a valid handle. In other words, it won’t be null if no error is returned.
CMProfileResponse data structure
Date Written: 3/18/93
Last reviewed: 6/24/93
How do I access fields within the CMProfile data structure?
___
Use the structures defined in the CMApplication.h file. These structures will allow you to directly access all fields up to the profileName. You can’t access the profileName directly because the CMProfileResponse record is variable size. You can determine the profileName by using the GetProfileName call. Likewise, the customData must also be accessed in this way by using the GetProfileAdditionalDataOffset call.
ColorSync documentation
Date Written: 2/8/93
Last reviewed: 4/1/92
Do you have any documentation that describes ColorSync in more detailed than “The ColorSync Utilities” on the Developer CD? I want to develop a ColorSync-aware printer driver and application.
___
The “ColorSync Utilities” document on the Developer CD is a preliminary draft. Apple is updating it to include more sample code and descriptive explanations. The final document will be included as a chapter in Apple’s forthcoming Inside Macintosh: QuickDraw reference, which will be available later this year.
“ColorSync: A Tuning Fork for Color,” in the February issue of Apple Direct, provides a ColorSync overview. Also, the Print Hints column in the June 1993 issue of develop, “ColorSync and Printing,” addresses ColorSync implementation issues for products such as yours.
How Apple Color Printer uses ColorSync
Date Written: 2/8/93
Last reviewed: 6/14/93
What’s the mechanism the Apple Color Printer driver uses for ColorSync?
___
The Apple Color Printer uses the low-level ColorSync color-matching calls to perform color matching in the print bottleneck routines. Thus, color matching happens before rendering so that rendering always occurs with matched colors. For more information, see the section called “What does a printer driver have to do?” in the Print Hints column of the June 1993 issue of develop.
ColorSync 1.0.2 fixes kNumColorsToMatch bug
Date Written: 1/20/93
Last reviewed: 6/14/93
I tried calling CMMatchColors with different numbers of colors to match; 10,000 worked fine, but 50,000 or greater crashed with a bus error. Is this a bug?
___
This is a bug in ColorSync 1.0 and 1.0.1. The “for” loop inside the Apple CMM uses a short rather than a long. It’s been fixed for version 1.0.2.
ColorSync responders
Date Written: 1/18/93
Last reviewed: 6/14/93
Which ColorSync responders can I rely on? The ColorSync cdev as I understand it yields the system responder. I don’t have anything else (for example, Color Printer driver); how do I get a monitor and printer responder?
___
The ColorSync documentation doesn’t discuss responders very well. A system profile responder is always available if ColorSync is installed. It can be used only to set the current system profile and to get the current system profile. A printer profile isn’t registered normally and has to be registered by the printer driver when requested to do so with a PrGeneral call.
For more information, please read “Syncing up with ColorSync” in issue 14 of develop.
MatchColors and profile handles for CMYK or RGB conversion
Date Written: 1/18/93
Last reviewed: 6/14/93
How do I get the right ProfileHandles to convert CMYK to RGB data and vice versa via the low-level MatchColors call? All routines now are returning “Responder errors” (-180 and -182), a description that doesn’t help much at the moment.
___
To convert CMYK to RGB, you need to have a proper source profile specifying that the data is in CMYK format (dataType = "CMYK"), and a proper destination profile specifying that the data is in RGB format (dataType = "RGB "). You can use ProfileMaker to modify a current profile (that is, 13" RGB) to create the CMYK format; or, you can also use the current system profile obtained by calling GetProfile.
QD 525 - LaserWriter Utility Q&As
QuickDraw
Revised by: Developer Support Center September 1993
Written by: Developer Support Center October 1990
This Technical Note contains a collection of Q&As relating to a specific topic—questions you’ve sent the Developer Support Center (DSC) along with answers from the DSC engineers. While DSC engineers have checked the Q&A content for accuracy, the Q&A Technical Notes don’t have the editing and organization of other Technical Notes. The Q&A function is to get new technical information and updates to you quickly, saving the polish for when the information migrates into reference manuals.
Q&As are now included with Technical Notes to make access to technical updates easier for you. If you have comments or suggestions about Q&A content or distribution, please let us know by sending an AppleLink to DEVFEEDBACK. Apple Partners may send technical questions about Q&A content to DEVSUPPORT for resolution.
SCSI drive format for LaserWriter II NTX with Rev. 3 ROMs
Date Written: 5/3/91
Last reviewed: 6/26/91
What could be causing intermittent PostScript errors from a LaserWriter II NTX with an SCSI hard disk mounted after installing the Rev 3 ROM upgrade?
___
The LaserWriter II NTX with Rev 3 ROMs does not work properly with a hard disk formatted with the LaserWriter Font Utility 1.x. You need to reformat the disk and copy the fonts back onto the hard disk using the LaserWriter Font Utility 2.0.2. Unfortunately, there is no way to make this procedure easier.
LaserWriter Font Utility copies Postscript file to printer
Date Written: 9/23/91
Last reviewed: 10/8/91
Is there a utility that will send a PostScript file from disk to the LaserWriter?
___
The LaserWriter Font Utility handles this job and is part of the System 7 disk set. On the System 7 Golden Master CD, the path is System 7.0: System Software 7.0: Installer Version: Tidbits folder.
QD 530 - Palette Manager Q&As
QuickDraw
Revised by: Developer Support Center September 1993
Written by: Developer Support Center October 1990
This Technical Note contains a collection of Q&As relating to a specific topic—questions you’ve sent the Developer Support Center (DSC) along with answers from the DSC engineers. While DSC engineers have checked the Q&A content for accuracy, the Q&A Technical Notes don’t have the editing and organization of other Technical Notes. The Q&A function is to get new technical information and updates to you quickly, saving the polish for when the information migrates into reference manuals.
Q&As are now included with Technical Notes to make access to technical updates easier for you. If you have comments or suggestions about Q&A content or distribution, please let us know by sending an AppleLink to DEVFEEDBACK. Apple Partners may send technical questions about Q&A content to DEVSUPPORT for resolution.
New Q&As in this Technical Note:
GetNewPalette and default palette documentation update
GetNewPalette and default palette documentation update
Date Written: 3/10/93
Last reviewed: 6/24/93
Our application has a 'pltt' resource containing colors, which is replaced by the user’s palette from a Preferences file if it exists. Sometimes our Color dialog box displays black-and-white or incorrect colors. Does the Palette Manager documentation describe default palettes correctly? Should I call GetNewPalette(0) on my application resource fork instead of calling GetPalette(-1)?
___
The actual implementation of the Palette Manager is somewhat different than documented in Inside Macintosh Volume VI.
The Palette Manager has two levels of default palettes. First, there’s a system palette that’s loaded at system startup time. This system palette ('pltt', ID#0) is loaded from the system file if it exists and stored in the system heap along with other Palette Manager globals. Once the system is booted, this system palette doesn’t change. Even if you replace the palette in the system file, the new palette isn’t reloaded. If there’s no system palette resource in the system file, the Palette Manager hard codes a 2 entry (black and white) palette as the system palette. The default system file for both Systems 6.0.8 and 7.1 doesn’t have a ('pltt', ID#0) resource. Therefore, it seems that the default system palette is typically an entry palette with black and white.
The other level of default palette is the application palette. This palette is stored in a low-memory global called AppPalette. You probably won’t find it in any documentation, but the address is $DCC. (This low-memory global is switched during context switches so you don’t have to worry about losing the low-memory global when switching applications.) The Palette Manager loads this default application palette when InitPalette is called. Normally, you don’t call InitPalette directly in your code. Instead, InitWindows calls InitPalette for you. Inside InitPalette, a call to Get1Resource is made with ('pltt', ID#0). Get1Resource differs from GetResource because Get1Resource checks only the first resource file. Since your application is assumed to be at the top of the resource chain, this call will get a palette of ID #0 if it’s available in that file *only*. This actually causes a problem when running an application from a development environment such as Think C.
Unlike the default system palette, the default application palette can be modified by a call to SetPalette(WindowPtr(-1), newDefaultAppPalette, TRUE). You can also get the default palette with a call to GetPalette(WindowPtr(-1)). When there’s no application default palette, GetPalette(WindowPtr(-1)) uses the default system palette instead. As mentioned earlier, this is typically a 2 entry palette with black and white.
Lastly, the documentation for the call GetNewPalette on page 20-19 of Inside Macintosh Volume VI is completely misleading. GetNewPalette simply loads in the specified 'pltt' resource using GetResource and then detaches it with DetachResource to make it a handle. It doesn’t attach it to the current window. And, it doesn’t load the default application handle if the specified resource can’t be found.
SetPalette cUpdates, NSetPalette, and window update events
Date Written: 9/26/91
Last reviewed: 6/14/93
When I pass FALSE in the cUpdates parameter to SetPalette, I still get update events to that window when I modify its palette. What’s going on?
___
SetPalette’s cUpdates parameter controls whether color-table changes cause that window to get update events only if that window is not the frontmost window. If that window is the frontmost window, any changes to its palette cause it to get an update event regardless of what the cUpdates parameter is. When you call SetEntryColor and then ActivatePalette for your frontmost window, the window gets an update event because it’s the frontmost window even though you passed FALSE in the cUpdates parameter. Another important point is that windows that don’t have palettes always get update events when another window’s palette is activated.
Fortunately, system software version 6.0.2 introduced the NSetPalette routine, which is documented in the Macintosh Technical Note “Palette Manager Changes in System 6.0.2” and on page 20-20 in the Palette Manager chapter of Inside Macintosh Volume VI. This variation of SetPalette gives you the following options in controlling whether your window gets an update event:
• If you pass pmAllUpdates in the nCUpdates parameter, your window gets an update event when either it or another window’s palette is activated.
• If you pass pmFgUpdates, your window gets an update event when a palette is activated only if it’s the frontmost window (in effect, it gets an update event only if its own palette is activated).
• If you pass pmBkUpdates, your window gets an update event when a palette is activated only if it’s not the frontmost window (in effect, it gets an update event only if another window’s palette is activated).
• If you pass pmNoUpdates, your window never gets an update event from any window’s palette being activated, including that window itself.
Restoring Finder desktop colors after using Palette Manager
Date Written: 8/28/91
Last reviewed: 8/1/92
After using the Macintosh Palette Manager, how do I restore the Finder’s desktop colors?
___
The Finder desktop’s colors are restored automatically on quitting applications that use the Palette Manager. Colors aren’t restored automatically when switching from your application to another, but if that application needs a certain set of colors and uses the Palette Manager to get them, then it’ll have them the moment it comes to the front. If you’re concerned about applications that don’t use the Palette Manager, you can use RestoreDeviceClut(gd:GDHandle), passing the handle to the GDevice of the screen you want to reset, or nil if you want to reset all of your devices. Passing nil to RestoreDeviceClut is your best bet, as it is very straightforward, and resets all of your monitors. You may not wish to do this, however, because RestoreDeviceClut is only available on machines with 32-bit color QuickDraw.
To reset a screen’s GDevice for machines without 32-bit color QuickDraw, you will need to keep track of the color table.When your application starts up, get the GDevice’s color table and save it—you’ll need it later. This value can be found at (**(**GDHandle).gdPMap).pmTable, where gdPMap is a PixMapHandle, and pmTable is a CTabHandle which tells you the absolute colors for this image. These data structures are found in Inside Macintosh Volume V, pages 52 and 119.
Build your application’s “world” using the Palette Manager, and avoid low-level methods of changing colors. When your application is about to quit and you want to restore the environment to its original state, get the color table you saved in the beginning. Convert this to a palette using CTab2Palette. Then set your window to this palette with SetPalette. This will cause the environment to update to the original color table that you initially got from the GDevice. If the application that is behind your application is Palette Manager friendly, then it will restore the environment to its palette. You may also want to do this procedure at the suspend event, as shown in the DTS sample MacApp program, FracApp. One of the problems that you won’t be able to solve this way involves multiple monitors. You won’t know which one to update. Only the monitor that has the window that you’ve called ActivatePalette on will update.
If your application changes the color environment with the Palette Manager, then RestoreDeviceClut is called automatically when your application quits. This means that you shouldn’t have to worry about restoring the palette if you don’t want to. There is a catch, however (there always is). When you use the SADE version of MultiFinder (6.1b9), it prevents this from automatically happening. Other versions of MultiFinder don’t have this side effect.
RestoreDeviceClut and color flash when application quits
Date Written: 8/23/91
Last reviewed: 6/14/93
When my application, which uses a color palette, quits, there is momentary but distracting flash of weird colors in the Finder windows and the desktop temporarily appears in a weird color. Is there any way to get around this?
___
When you quit, RestoreDeviceClut is called to restore the color table and an update event is called to redraw the screen. It’s the delay between the change in the color table and the update event that causes the flash of incorrect colors to be displayed. This, unfortunately, is unavoidable.
Macintosh Palette Manager and offscreen graphics
Date Written: 7/22/91
Last reviewed: 8/1/92
The Macintosh Palette Manager doesn’t work on offscreen environments the way you’d expect. Unlike color windows, SetPalette will not change the offscreen’s color table; rather it just allows you to use PMForeColor and PMBackColor to set the current drawing color in that environment. To change the offscreen’s color table you’ll need to convert the palette to a color table and then set the resulting color table to the off screen. Calling Palette2CTab will do the converting for you.
To get around having to use palettes to define the current drawing color in the off screen, you can always use Index2Color and then RGBForeColor to get the color to be set for drawing. A remake of GiMeDaPalette code sample, available on the latest Developer CD Series disc, does offscreen drawing in place of having to continually copy a PICT.
QD 535 - Picture Utility Q&As
QuickDraw
Revised by: Developer Support Center September 1993
Written by: Developer Support Center October 1990
This Technical Note contains a collection of Q&As relating to a specific topic—questions you’ve sent the Developer Support Center (DSC) along with answers from the DSC engineers. While DSC engineers have checked the Q&A content for accuracy, the Q&A Technical Notes don’t have the editing and organization of other Technical Notes. The Q&A function is to get new technical information and updates to you quickly, saving the polish for when the information migrates into reference manuals.
Q&As are now included with Technical Notes to make access to technical updates easier for you. If you have comments or suggestions about Q&A content or distribution, please let us know by sending an AppleLink to DEVFEEDBACK. Apple Partners may send technical questions about Q&A content to DEVSUPPORT for resolution.
GetPictInfo and QuickTime compressed PICT files
Date Written: 2/24/92
Last reviewed: 8/1/92
Is it my imagination, or does GetPictInfo return a bit depth of 1 on QuickTime compressed PICT files?
___
Yep! This is what’s happening: The Picture Utilities Package doesn’t know of the QuickTime Compressed Pixmap opcode (0x8200), so it just skips over the opcode’s data; then it finds the PacksBitRect opcode containing the black-and-white pseudo-alert that you get when you draw the picture on a machine that doesn’t have QuickTime installed, and GetPictInfo reports back this alert.
Trivia: When QuickTime is installed, it displays the compressed image and then ignores the following PacksBitRect since QuickTime knows it’s only the black-and-white alert.
NewPalette doesn’t use CTab2Palette to create a palette
Date Written: 3/12/92
Last reviewed: 6/14/93
I’m using the Picture Utilities Package to extract the color table from a picture. After getting the color table, I use NewPalette to construct a palette from the color table (usage = tolerant, tolerance = 0). After I do this, the RGB values in the palette don’t always exactly match the RGB values in the source color table, causing my program to fail. If I use NewPalette without a source color table, and then use CTab2Palette to copy the colors over (again with usage = tolerant, tolerance = 0), the colors match exactly.
___
It turns out that NewPalette doesn’t use CTab2Palette, but copies the RGB fields in a strange way that’s causing the problems you’re seeing. NewPalette copies the high byte in each color table RGB entry into both the high byte and the low byte of the corresponding palette entry. Thus, if the color table entry for red was $F000, it becomes $F0F0. This of course makes no difference to QuickDraw since the low byte isn’t displayed, but if your program expects the low byte to match, that’s where your problem exists. CTab2Palette is different, in that it doesn’t copy the high byte into the low byte unless the pmAnimated bit is set.
The best solution for your code isn’t to compare the entire RGB value when comparing colors, but rather to compare the high byte of each RGB component separately. If this isn’t possible, the next best solution is for you to use the workaround that you’ve already discovered with CTab2Palette.
It’s unlikely that the Palette Manager is going to change in the future for something like this. In fact, we would almost call it a “feature” since other developers may even depend on it.
Spooling PixMaps to disk
Date Written: 6/10/91
Last reviewed: 10/22/91
Do you have sample code for spooling PixMaps to disk in PICT format? Should I write the PICT opcodes to the file myself?
___
Apple recommends that you do not try to write the PICT opcodes yourself. Instead, replace the PutPicProc bottleneck proc, as shown in the Color QuickDraw chapter of Inside Macintosh Volume V on page V-89.
Two additional samples can be found in the sample code contained in the Developer CDs. Look for: “Tools & Apps: Graphics and Imaging: PICT Stuff.” One is a program and the other is an FKEY; both dump the main screen to disk as a PICT. The FKEY is a more complete sample in the sense that it works in black and white as well as Macintosh color computers, but the other is a smaller and simpler sample.
Getting the color usage from a picture under System 6
Date Written: 6/8/92
Last reviewed: 9/15/92
Do you know how I can obtain the color table of a picture when using a system version that happens to be less than 7.0? The Picture Utilites package seems to be only implemented in System 7.0.
___
You’re correct; the Picture Utilities package is implemented only under System 7. However, it’s possible to write code to duplicate its functionality under System 6. Basically, what you want to do is parse a picture, looking at the colors used for the different objects. How you deal with the colors is up to you.
What you do is replace the QuickDraw bottlenecks in a GrafPort with procedures of your own; in all the bottlenecks for QuickDraw primitives, you can just record the current color as having been used for an object. When you get a StdBits opcode, you’ll have to parse the pixmaps, looking through the image and recording all the colors used. As a shortcut, you could just record all the colors in the color table of the pixmap, if it's an indexed pixel image. After collecting this list of colors and any information on how often they are used, it’s up to you to boil this down into useful information, depending on how you want to use it.
QD 540 - QuickDraw GX Q&As
QuickDraw
Revised by: Developer Support Center September 1993
Written by: Developer Support Center October 1990
This Technical Note contains a collection of Q&As relating to a specific topic—questions you’ve sent the Developer Support Center (DSC) along with answers from the DSC engineers. While DSC engineers have checked the Q&A content for accuracy, the Q&A Technical Notes don’t have the editing and organization of other Technical Notes. The Q&A function is to get new technical information and updates to you quickly, saving the polish for when the information migrates into reference manuals.
Q&As are now included with Technical Notes to make access to technical updates easier for you. If you have comments or suggestions about Q&A content or distribution, please let us know by sending an AppleLink to DEVFEEDBACK. Apple Partners may send technical questions about Q&A content to DEVSUPPORT for resolution.
New Q&As for this month:
QuickDraw GX doesn’t replace QuickDraw
QuickDraw GX and QuickDraw accelerated display cards
QuickDraw GX doesn’t replace QuickDraw
Date Written: 12/11/92
Last reviewed: 7/2/93
Will QuickDraw and QuickDraw GX coexist, or will QuickDraw GX replace QuickDraw?
___
QuickDraw isn’t leaving with the introduction of QuickDraw GX. It’s here to stay. Among other reasons, QuickDraw functions are used extensively by the Macintosh Toolbox and applications. For more information, see the article “Getting Started With QuickDraw GX” in issue 15 of develop.
QuickDraw GX and QuickDraw accelerated display cards
Date Written: 6/16/92
Last reviewed: 7/2/93
Will display cards that offer QuickDraw acceleration be affected by QuickDraw GX?
___
Current QuickDraw acceleration cards aren’t affected by QuickDraw GX. QuickDraw GX doesn’t use any of the current QuickDraw calls and its presence won’t affect applications that use only QuickDraw. QuickDraw acceleration cards also won’t accelerate QuickDraw GX.